y3-lualib Lua Development Framework

repository·main·Indexed 41 days ago

https://github.com/y3-editor/y3-lualib

A Lua development framework for the Y3 Editor that allows developers to create maps more efficiently than using the standard ECA system. It includes core game logic implementations, a behavior tree system via LuaNPBehave, and specialized timing modules (ClientTimer, Timer, LocalTimer). The framework integrates with the Y3 Development Assistant for debugging and hot reloading, and provides a comprehensive API for managing game objects such as Abilities and CustomEvents.

Tokens
74.6K
Snippets
300
Records
441
Agent score
45%

What's inside y3-lualib

  1. Overview of LuaNPBehave

    main

    LuaNPBehave is a Lua implementation of NPBehave. It maintains a largely consistent API with the original project, but includes specific renaming of certain functions to avoid ambiguity in Lua.

    The library features a robust type system and is compatible with the sumneko.lua (Lua Language Server) language analysis service plugin for enhanced development support.

  2. Understand the y3-lualib project structure

    main

    The project is organized into several functional directories:

    • 演示/: Demo code and examples.
    • game/: Core game logic implementations:
      • const.lua: Constants and enums.
      • game.lua: Game feature interfaces.
      • helper.lua: Common glue functions.
      • py_converter.lua: Conversion between engine objects and Lua objects.
      • py_event_subscribe.lua: Binding engine event system to Lua event system.
    • meta/: Engine API meta files.
    • object/: Lua object implementations, categorized into:
      • editable_object/: Objects editable via object data (物编).
      • runtime_object/: Abstract objects existing only at runtime.
      • scene_object/: Objects placed within a scene.
    • tools/: General Lua tools and engine-specific tools.
    • debugger.lua: Debugger configuration.
    • init.lua: The main entry point for the Y3 library.
  3. Use PyProxy to manage Python object handles

    main
    The PyProxy module provides a set of functions to manage, wrap, and interact with Python object handles within the Lua environment. It allows for safe retrieval, destruction, and conversion of handles between different states.
  4. Use the Y3 Development Assistant Game API

    main
    The Y3 Development Assistant provides a protocol for interacting with the game environment. This protocol is split into two main contexts: commands sent from the Game to the Assistant (to control the UI/Editor) and commands sent from the Assistant to the Game (to execute logic or retrieve data).
  5. Use NPBehave Decorators to modify node execution

    main

    Decorators in NPBehave wrap a Decoratee (an NPBehave.Node) to alter its behavior. Common decorator types include:

    • Inverter: Inverts the result of the decorated node.
    • Observer: Observes the decorated node.
    • ObservingDecorator: An abstract base for decorators that observe changes, requiring implementation of IsConditionMet, StartObserving, and StopObserving.
    • Random: Selects a child or behavior randomly.
    • Repeater: Repeats the execution of the decorated node.
    • Hook: Provides hooks like DoStart, DoCancel, and DoChildStopped to intercept lifecycle events.

    All decorators inherit common properties like Blackboard, Clock, Label, Name, and CurrentState.

  6. Use CustomEvent for Receipt and Notification patterns

    main

    The CustomEvent system provides two distinct ways to handle events: Receipt (Callback) Mode and Notification Mode.

    Receipt Mode (event_dispatch)

    Allows for logic insertion and returns values. When multiple callbacks are registered, they are called in order. If any callback returns a non-nil value, subsequent triggers are skipped.

    Notification Mode (event_notify)

    A standard notification. If an event is triggered while another is executing, the new event is queued.

    Registering Events (event_on)

    You can register callbacks. You can optionally specify an expected argument pattern to ensure the event only triggers when arguments match.

    Example: Receipt Mode

    Obj:event_on('获取', function (trigger,...) 
        return 1 
    end)
    
    Obj:event_on('获取', function (trigger,...) 
        print('This won't run') 
    end)
    
    local result = Obj:event_dispatch('获取') -- result will be 1

    Example: Notification Mode

    Obj:event_on('获得', function () 
        print('Triggered') 
        Obj:event_notify('移除') -- This will be queued if '获得' is still processing
    end)
    
    Obj:event_notify('获得')

    Example: Argument Filtering

    -- Only triggers if the first argument is '123'
    Obj:event_on('输入', {'123'}, function (trigger, ...) 
        print('Matched!') 
    end)
    
    Obj:event_notify('输入', 1) -- Does NOT trigger
    Obj:event_notify_with_args('输入', {'123'}, 2) -- Triggers
    Obj:event_on('input', function(trg, ...) print('Event fired') end)
    Obj:event_notify('input')
  7. Understand the Cast object

    main
    The Cast object represents a specific instance of a spell or ability being cast. It is passed as an argument within casting-related events. It allows you to inspect which ability is being used, the direction of the cast, and the specific target (whether it be a unit, item, destructible object, or point in space).
  8. Implement a custom NPBehave.Node

    main

    When creating custom nodes, you must implement the lifecycle methods. Note that Stopped(success) must be the absolute last call in your function; do not modify any state after calling it.

    -- Essential lifecycle methods to override:
    -- DoStart()
    -- DoCancel()
    -- Stopped(success)
    -- DoParentCompositeStopped(composite)
  9. Use Custom Events with Notify (Notification Mode)

    main

    The event_notify method implements a 'Notification Mode' (通知模式). This is used for broadcasting events without expecting a return value.

    Key behaviors:

    • Only one event can be executing on a single object at a time.
    • If a new event is triggered while another is currently processing (e.g., during an insertion settlement), the new event is placed into a queue to be executed later.
    • Use event_notify_with_args to pass arguments to the listeners.
    Obj:event_on('获得', function ()
        print('触发获得')
        Obj:event_notify('移除') -- This will be queued if '获得' is still running
        print('发起移除后')
    end)
    
    Obj:event_on('移除', function ()
        print('触发移除')
    end)
    
    Obj:event_notify('获得')
    -- Output:
    -- 触发获得
    -- 发起移除后
    -- 触发移除
  10. Use the DamageInstance object in damage events

    main

    The DamageInstance object is passed as a parameter in damage-related events (e.g., EventParam.单位-受到伤害后). It allows you to inspect and modify the properties of a specific damage occurrence, such as the amount of damage, whether it was a critical hit, or if it was missed.

    -- Example context: inside a damage event handler
    function OnUnitDamaged(unit, damageInstance)
        local damage = damageInstance:get_damage()
        print("Unit took " .. damage .. " damage")
    end
  11. Use HealInstance in healing events

    main

    The HealInstance object is passed as a parameter during healing-related events. It allows you to inspect and modify the healing amount and the associated ability.

    It is specifically provided in the following event parameter:

    • EventParam.单位-受到治疗后 (Unit received healing)
  12. Use LocalTimer for flexible timing

    main
    The LocalTimer supports both asynchronous creation and callbacks. If executed synchronously, it ensures synchronous callbacks. Use this when you need more flexibility than a standard Timer but want to manage synchronization risks yourself.