2D 横版游戏开发教程编写 UI 模块编写 UI 模块 在这篇教程中,我们将介绍如何使用Dora SSR游戏引擎创建游戏的UI模块。我们将使用两种方式创建UI:一种是使用基于游戏场景节点编写的UI功能组件,一种是使用ImGui框架的接口。然而,需要注意的是,ImGui在实际开发中并不推荐直接用于创建游戏UI,而是建议主要用于开发一些游戏辅助的调试UI。 首先,我们需要引入所需的模块和库: Script/UI.tllocal Platformer <const> = require("Platformer")local ImGui <const> = require("ImGui")local Vec2 <const> = require("Vec2")local Director <const> = require("Director")local AlignNode <const> = require("UI.Control.Basic.AlignNode")local CircleButton <const> = require("UI.Control.Basic.CircleButton")local App <const> = require("App")local Group <const> = require("Group")local Menu <const> = require("Menu")local AlignNode <const> = require("AlignNode")local Keyboard <const> = require("Keyboard")local Loader <const> = require("Script.Loader")local Sprite <const> = require("Sprite")local Spawn <const> = require("Spawn")local Opacity <const> = require("Opacity")local Y <const> = require("Y")local type Entity = require("Entity")local type UnitType = Platformer.Unit.Type 接下来,我们定义了一个函数updatePlayerControl,用于更新玩家的控制状态。这个函数接收一 个键名和一个布尔值,表示是否按下了这个键。如果按下了这个键,那么玩家的这个键的状态就会被设置为true,否则就会被设置为false。 Script/UI.tllocal keyboardEnabled = truelocal playerGroup = Group{"player"}local function updatePlayerControl(key: string, flag: boolean, vpad: boolean) -- 如果按下屏幕虚拟按键就不再检测键盘按键 if keyboardEnabled and vpad then keyboardEnabled = false end -- 把按键状态数据分发到玩家数据实体上等待处理 playerGroup:each(function(self: Entity.Type): boolean self[key] = flag end)end 然后,我们创建了一个UI的根节点ui,并将其添加到Director.ui中。这个节点是所有其他UI节点的父节点。 Script/UI.tl-- 创建一个使用 Flex 布局的布局根节点local ui = AlignNode(true)ui:css('flex-direction: column-reverse')ui:addTo(Director.ui)-- 创建虚拟按键区域的布局节点local bottomAlign = AlignNode()bottomAlign:css([[ height: 80; justify-content: space-between; padding: 0, 20, 20; flex-direction: row]]);bottomAlign:addTo(ui) 接着,我们创建了一个左对齐的节点leftAlign,并将其添加到bottomAlign中。然后,我们在leftAlign中创建了一个菜单leftMenu,用于放置屏幕左边的操作按钮。然后同样创建一个右对齐的菜单用于放置屏幕右边的操作按钮。 Script/UI.tl-- 创建左对齐的菜单local leftAlign = AlignNode()leftAlign:css('width: 130; height: 60')leftAlign:addTo(bottomAlign)local leftMenu = Menu()leftMenu.size = Size(250, 120)leftMenu.anchor = Vec2.zeroleftMenu.scaleX = 0.5leftMenu.scaleY = 0.5leftMenu:addTo(leftAlign)-- 创建右对齐的菜单local rightAlign = AlignNode()rightAlign:css('width: 60; height: 60')rightAlign:addTo(bottomAlign)local rightMenu = Menu()rightMenu.size = Size(120, 120)rightMenu.anchor = Vec2.zerorightMenu.scaleX = 0.5rightMenu.scaleY = 0.5rightMenu:addTo(rightAlign) 在leftMenu中,我们创建了三个圆形按钮:leftButton、rightButton和jumpButton。这三个按钮分别用于控制玩家的左移、右移和跳跃操作。每个按钮都有一个TapBegan事件和一个TapEnded事件,分别在按钮被按下和按钮被释放时触发。 Script/UI.tl-- 创建左移按钮local leftButton = CircleButton { text = "左(a)", radius = 60, fontSize = 36}leftButton.anchor = Vec2.zeroleftButton:slot("TapBegan", function() updatePlayerControl("keyLeft", true, true)end)leftButton:slot("TapEnded", function() updatePlayerControl("keyLeft", false, true)end)leftButton:addTo(leftMenu)-- 创建右移按钮local rightButton = CircleButton { text = "右(d)", x = 130, radius = 60, fontSize = 36}rightButton.anchor = Vec2.zerorightButton:slot("TapBegan", function() updatePlayerControl("keyRight", true, true)end)rightButton:slot("TapEnded", function() updatePlayerControl("keyRight", false, true)end)rightButton:addTo(leftMenu)-- 创建跳跃按钮local jumpButton = CircleButton { text = "跳(j)", radius = 60, fontSize = 36}jumpButton.anchor = Vec2.zerojumpButton:slot("TapBegan", function() updatePlayerControl("keyJump", true, true)end)jumpButton:slot("TapEnded", function() updatePlayerControl("keyJump", false, true)end)jumpButton:addTo(rightMenu) 接下来,我们使用ImGui创建了一个背包窗口。在这个窗口中,我们可以看到玩家的背包中的所有物品,以及每个物品的数量和描述。当玩家点击一个物品时,这个物品的数量就会减少1,同时在玩家的角色上生成一个对应的精灵。 Script/UI.tllocal pickedItemGroup = Group{"picked"}local windowFlags = { "NoDecoration", "AlwaysAutoResize", "NoSavedSettings", "NoFocusOnAppearing", "NoNav", "NoMove"}local themeColor = App.themeColorDirector.ui:schedule(function(): boolean local size = App.visualSize ImGui.SetNextWindowBgAlpha(0.35) ImGui.SetNextWindowPos(Vec2(size.width - 10, 10), "Always", Vec2(1, 0)) ImGui.SetNextWindowSize(Vec2(100, 300), "FirstUseEver") ImGui.Begin("BackPack", windowFlags, function() if ImGui.Button("重新加载Excel") then Loader.loadExcel() end ImGui.Separator() ImGui.Dummy(Vec2(100, 10)) ImGui.Text("背包") ImGui.Separator() ImGui.Columns(3, false) -- 遍历有被标记拾取状态picked组件的道具实体 pickedItemGroup:each(function(e: Entity.Type): boolean local item = e as Loader.ItemEntity if item.num > 0 then -- 当按下道具按钮时进行处理 if ImGui.ImageButton("item" .. tostring(item.no), item.icon, Vec2(50, 50)) then item.num = item.num - 1 local sprite = Sprite(item.icon) if not sprite is nil then sprite.scaleX = 0.5 sprite.scaleY = 0.5 sprite:perform(Spawn( Opacity(1, 1, 0), Y(1, 150, 250) )) local player = playerGroup:find(function(): boolean return true end) local unit = player.unit as UnitType unit:addChild(sprite) end end -- 当指针在道具按钮上悬浮时进行处理 if ImGui.IsItemHovered() then ImGui.BeginTooltip(function() ImGui.Text(item.name) ImGui.TextColored(themeColor, "数量:") ImGui.SameLine() ImGui.Text(tostring(item.num)) ImGui.TextColored(themeColor, "描述:") ImGui.SameLine() ImGui.Text(tostring(item.desc)) end) end ImGui.NextColumn() end end) end) return falseend) 以上就是我们的UI模块的全部内容,UI功能的程序开发往往会比较繁杂,但其实都是一些重复性比较高但不困难的代码。的在这个模块中,我们创建了一个基于游戏场景节点的UI,用于控制玩家的移动和跳跃操作,以及一个基于ImGui的UI,用于显示玩家的背包内容。到这里我们的教程已经接近尾声了,加油,通过下一篇教程我们就能把完整的游戏跑起来啦。