Dan aksi...
Semuanya aksi dan reaksi

Yaml
Oleh sebab saya tidak mahu menulis editor permainan untuk menjimatkan masa, saya memutuskan untuk menggunakan fail yaml. Kelebihannya, fail ini juga boleh menggunakan rujukan untuk mengelakkan pengulangan. Dan pada masa depan saya mungkin boleh membuat editor yang mengeksport fail yaml.
Dalam fail yaml perlu diterangkan setiap tindakan yang boleh berlaku bagi setiap babak dan watak. Di bawah anda boleh lihat sebahagian daripada scene20.yml. Di situ anda nampak hujan turun selama 5 saat dan teks dipaparkan semasa babak bermula. Anda juga nampak beberapa tindakan hover dan apa yang perlu berlaku apabila anda memilih contohnya tindakan “buka” “papan maklumat”.
# 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
Sesuatu entity juga boleh mempunyai tindakan yang berbeza daripada tindakan global babak. Ini dilakukan dengan menambah perkara berikut di bahagian bawah fail.
*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?
Rujukan semua entity, contohnya *dolmens, diterangkan dalam assets/refs.yaml dan boleh saya gunakan semula di mana-mana. Teks text: ditukar kepada kunci dan jika perlu diterjemahkan. Di bawah ialah sebahagian daripada 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
...
Saya boleh membuat tindakan tersuai untuk apa sahaja dan mengendalikannya dalam Lua. Senarai tindakan buat masa ini adalah seperti di bawah.
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
Pemprosesan tindakan
Fail yaml ditukar kepada json dan kemudian kepada fail messagepack, kerana fail itu jauh lebih kecil dan diproses jauh lebih pantas.
Selepas itu barulah kerja sebenar, tetapi ia sukar ditunjukkan dengan jelas di sini kerana action.lua mengandungi kira-kira 600 baris kod, dan ada juga action-event.lua dan action-status.lua.
Dalam action-event.lua ada logik sama ada pemain telah mengklik butang tindakan seperti ‘ambil’ dan kemudian contohnya sebiji epal, ‘selectedItem’. Atau ‘beri kepada’ > selectedActor. Di sini diterangkan semua logik tentang apa yang boleh dilakukan pemain (bersama tindakan yang diterangkan dalam yaml). Akhirnya tindakan itu dilaksanakan.
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
}
)
Terlalu banyak isi dalam action.lua, tetapi di bawah ialah beberapa bahagian penting. Untuk setiap jenis tindakan yaml saya membuat objek khas yang mengandungi semua logiknya. Objek ini digunakan semula secara berterusan.
-- 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
Bunyi
Satu contoh ialah jenis tindakan audio. Kelas logiknya terletak dalam framework.core.action.audio.lua dan dikawal daripada fail action/.yml dengan cara berikut
*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
Akhir kata
Semuanya disusun supaya engine3 dan permainan terpisah. Ini perlu supaya ia mudah diurus. Jika anda memprogram semua tindakan secara tetap dalam permainan, anda akan berdepan masalah besar. Lebih baik membuat fungsi generik yang boleh memproses semua tindakan.