Logo Signal From The Stars

En actie...

Alles is actie reactie

Martin avatar
  • Martin
  • 6 min read
Yes i'm using a white background

Yaml

Aangezien ik geen game editor wou schrijven om tijd te besparen, had ik besloten om yaml files gebruiken. Het voordeel is dat deze ook referenties kunnen gebruiken om herhaling te voorkomen. En in de toekomst zou ik eventueel een editor kunnen maken, die yaml bestanden exporteert.

Wat beschreven moest worden in de yaml bestanden was elke actie die zou kunnen plaatsvinden per scene en character. Hieronder zie je een dergelijk stukje van scene20.yml. Hierin zie je dat het 5 seconde gaat regenen en dat er een tekst zichtbaar wordt tijdens het starten van de scene. Ook zie je een aantal hover acties en wat er moet gebeuren wanneer je bijv. de actie “open” “informatie board” kiest.

# START scene
# ==================================================
*action-scene-entities-ready-after:
  - <<: *particle-system
    <<: *particle-system-settings-rain
    id: 200143
    ukey: *particle-rain-20
    stopTime: 5

*action-scene-start:
  - <<: *trigger
    id: 200144
    run_once: true
    methods:
      - *particle-rain-20:
        - start: nil
  - <<: *text
    id: 200104
    run_once: true
    text: "The Netherlands, Drente\n52Β°52'3.95\"N, 6Β°52'15.01\"E"
    <<: *tween-text-bottom-left-fade-in-fade-out

# HOVER
# ==================================================
*action-hover:
  *information-board1: Information board
  *portal-key: An old stone with inscriptions
  *branch: A tree root
  *pole: Pole
  *broken-information-board1: Broken information board
  *dolmens: Dolmens
  *stone-circle: Stone circle
  *hotspot-20-exit: Go home
  *hotspot-20-stone-circle: The center of the stone circle

# GLOBAL OPEN
# ==================================================
*action-open:
  *information-board1:
    - <<: *say
      id: 200007
      text: You cannot open an information board, it is a wooden panel with text and images
  *portal-key:
    - <<: *walk
      id: 200105
    - <<: *say
      id: 200008
      text: I cannot open the stone

*action-lookat:
  *portal-key:
    - <<: *walk
      id: 200121
    - <<: *say
      id: 200041
      text: It looks like an old stone with something written on it, it looks very interesting

Het is ook mogelijk dat een entity andere acties krijgt dan de globale beschreven acties van de scene, dit doe je doormiddel van onderin het bestand te het volgende te doen.

*diana:
  *action-pickup:
    *dolmens:
      - <<: *walk
        id: 200094
        target:
          x: 600
          y: 175
          framesetIndex: *move-up
      - <<: *say
        id: 200095
        text: What do you think? Do you really think I could lift that?

De referencies van alle entities bijv. *dolmens staan beschreven in assets/refs.yaml en kan ik overal opnieuw hergebruiken. De teksten text: worden omgezet naar keys en worden eventueel vertaald. Hieronder zie je een deel van de refs.yaml

...

# tweens
# fade in, show text 3 seconds, fade out and move to the left
tween-text-bottom-left-fade-in-fade-out: &tween-text-bottom-left-fade-in-fade-out
  textArguments:
    color: *color-white-hidden # going to fadein, using the tweens
    scale: .50 # @todo *scale-half-size
  font: *font-ui-text
  xOffset: *offset-left
  yOffset: *offset-bottom
  yOffsetIncludeUi: true # offset including menus (above menu)
  offsetMargin: [0,0,5,5] # top, right, bottom, left @todo global margin
  tweens:
    - duration: 2
      properties: 
        color: *color-white
    - duration: 1
      properties:
        color: *color-white-hidden
        x: -10
      wait: 3

# particle schortcuts
particle-system-settings-rain: &particle-system-settings-rain
  emissionRate: 200
  speed: 
    - 300
    - 400
  linearAcceleration:
    - 0
    - 800
    - 0
    - 1000
  sizes:
    - 1
    - 0.5
    - 0.2
    - 0.1
    - 0.8
  sizeVariation: 1
  emitterLifetime: -1
  particleLifetime:
    - 0.8
    - 1.5
  colors:
    - 0 # r
    - 0 # g
    - 0.3 # b
    - 0.8 # a
    - 0 # r
    - 0 # g
    - 0.8 # b
    - 0.8 # a

# characters
nikki: &nikki 10
martin: &martin 11
diana: &diana 12

# -- scene 20
information-board1: &information-board1 20002
portal-key: &portal-key 20003
branch: &branch 20004
broken-information-board1: &broken-information-board1 20005
pole: &pole 20006
dolmens: &dolmens 20007
stone-circle: &stone-circle 20008
hotspot-20-stone-circle: &hotspot-20-stone-circle 20009
hotspot-20-exit: &hotspot-20-exit 20010
scene-menu-20: &scene-menu-20 20011
particle-rain-20: &particle-rain-20 20012
...

# -- scene 21
...

Van alles kan ik een custom action maken en laten afvangen in Lua. De lijst met acties is op dit moment het onderstaande.

action-open: &action-open 1
action-close: &action-close 2
action-give: &action-give 3
action-pickup: &action-pickup 4
action-lookat: &action-lookat 5
action-talkto: &action-talkto 6
action-push: &action-push 7
action-pull: &action-pull 8
action-use: &action-use 9
action-hover: &action-hover 30
action-walkto: &action-walkto 31
action-say: &action-say 32
action-say_options: &action-say_options 34
action-inventory: &action-inventory 35
action-cannot-walk-to-point: &action-cannot-walk-to-point 36
action-popup: &action-popup 37
action-animation: &action-animation 38
action-scene-start: &action-scene-start 39
action-scene-entities-ready-after: &action-scene-entities-ready-after 40
action-camera: &action-camera 41
action-text: &action-text 44
action-modify-action: &action-modify-action 45
action-hotspot: &action-hotspot 46
action-trigger: &action-trigger 47
action-statement: &action-statement 48
action-world-loaded: &action-world-loaded 49
action-use-direct: &action-use-direct 50
action-audio: &action-audio 51
action-particle-system: &action-particle-system 52

Verwerking van de acties

De yaml bestanden worden omgezet naar json en daarna in messagepack bestanden, die zijn namelijk vele malen kleiner en worden vele malen sneller verwerkt.

Daarna komt het echte werk, alleen is dat hier niet echt duidelijk weer te geven aangezien action.lua +/- 600 regels code is en er is ook nog een action-event.lua en action-status.lua.

In action-event.lua staat de logica of de speler een actie button heeft aangeklikt zoals ‘pak op’ en dan bijv. een appel ‘selectedItem’. Of ‘geef aan’ > selectedActor. Hier staat alle logica beschreven wat een speler allemaal kan doen (i.c.m. een beschreven yaml actie). Uiteindelijk zal de actie worden uitgevoerd.

local activeActor = entityManager:find({movableByUserInput = true}, "Player")
action:execute(
  {
      action = tonumber(self.selectedActionButton.name),
      initiator = #activeActor > 0 and activeActor[1] or nil,
      selectedActionButton = self.selectedActionButton,
      selectedActor = self.selectedActor,
      selectedItem = self.selectedItem,
      selectedInventoryItem = self.selectedInventoryItem,
      selectedInventoryItem2 = self.selectedInventoryItem2
  }
)

In action.lua staat te veel, maar hieronder enkele belangrijke onderdelen. Ik maak van elke yaml actie type een speciaal object, met daarin alle logicia. Dit object wordt constant opnieuw gebruikt.

-- intial only once for the whole game
function Action:new()
  self.actionObject = {}
  self.actionObject[Action.WALK_TO] = ActionWalkTo()
  self.actionObject[Action.SAY] = ActionSay()
  self.actionObject[Action.SAY_OPTIONS] = ActionSayOptions()
  self.actionObject[Action.INVENTORY] = ActionInventory()
  self.actionObject[Action.POPUP] = ActionPopup()
  self.actionObject[Action.ANIMATION] = ActionAnimation()
  self.actionObject[Action.CAMERA] = ActionCamera()
  self.actionObject[Action.TEXT] = ActionText()
  self.actionObject[Action.ACTION_MODIFY] = ActionModify()
  self.actionObject[Action.TRIGGER] = ActionTrigger()
  self.actionObject[Action.STATEMENT] = ActionStatement()
  self.actionObject[Action.AUDIO] = ActionAudio()
  self.actionObject[Action.PARTICLE_SYSTEM] = ActionParticleSystem()
end


function Action:processActionList(actionList, actionListIndex, triggerData, afterProcessActionListCallback)
   ...

    -- maybe this action may run once
    if self:didRunOnce(actionId, actionData) then
        -- maybe there is another (next) action in the list that don't have run_once
        self:processActionList(actionList, actionListIndex + 1, triggerData, afterProcessActionListCallback)
        return
    end

    -- going to start this action
    -- maybe there are children, update the status to QUEUE and set this actionId on WORKING
    -- this will create all needed action status objects
    for _, actionListObj in pairs(actionList) do
        local actionStatusObj = self:getActionStatusFromList(actionListObj.id)

        if actionListObj.id == actionId then
            self:getActionStatusFromList(actionListObj.id):setStatus(ActionStatus.WORKING)
        elseif actionStatusObj:getStatus() == ActionStatus.WORKING or actionStatusObj:getStatus() == ActionStatus.DONE then
            -- do nothing
        else
            self:getActionStatusFromList(actionListObj.id):setStatus(ActionStatus.QUEUE)
        end
    end

    -- start the action
    self.actionObject[actionData.action]:execute(
        triggerData,
        actionData,
        actionList,
        actionListIndex,
        actionId,
        afterProcessActionListCallback
    )
end

Geluid

Een voorbeeld is de action type audio, de logica class hiervoor staat in framework.core.action.audio.lua en wordt op de volgende manier aangestuurd vanuit de action/.yml bestanden

*action-use-direct:
  *inventory-strange-whistle:
    *martin:
      - <<: *audio
        id: 100072
        audiosetName: martin-play-strange-whistle.ogg
        audiosetStatus: *audioset-playing
---@class ActionAudioActionData
---@field wait? integer The time to wait before starting the next action, default 0 sec.
---@field run_once? boolean If you want to run this action once
---@field audiosetName string The audioset name "something.ogg"
---@field audiosetStatus integer<Audioset.IDLE|Audioset.PLAYING|Audioset.PAUSE>
---@field dontWaitForCallback? boolean Wait until the audioset is done playing default false

---@param triggerData triggerData
---@param actionData ActionAudioActionData
---@param actionList table
---@param actionListIndex integer
---@param actionId integer
---@param afterProcessActionListCallback function
function ActionAudio:execute(
    triggerData,
    actionData,
    actionList,
    actionListIndex,
    actionId,
    afterProcessActionListCallback)
    ---@type Audioset
    local audioset =
        entityManager:findOne(
        {
            name = actionData.audiosetName
        },
        "Audioset"
    )

    -- create the callbacks first
    if actionData.dontWaitForCallback ~= true then
        audioset.callbackAfterStopped["actionAudiosetCallbackAfterStopped"] = function(s, framekey)
            action:afterProcessActionListAction(
                self,
                triggerData,
                actionData,
                actionList,
                actionListIndex,
                actionId,
                afterProcessActionListCallback
            )
        end
    end

    audioset:setAudiosetStatus(actionData.audiosetStatus)

    audioset:play()

    if actionData.dontWaitForCallback == true then
        action:afterProcessActionListAction(
            self,
            triggerData,
            actionData,
            actionList,
            actionListIndex,
            actionId,
            afterProcessActionListCallback
        )
    end
end

return ActionAudio

Tot slot

Alles is zo opgezet dat het gescheiden is van de engine3 en het spel. Dat is nodig om het beheersbaar te maken. Indien je alle acties zou programmeren op een vaste manier in het spel, loop tegen enorme problemen aan. Beter is een het maken generieke functies die alle acties kunnen verwerken.


βœ‰οΈ Blijf op de hoogte!

Ontvang gratis de laatste ontwikkelingen en motiveer mij om dit avontuur tot een succes te maken!