From a86f0719fbbead31f6c11148ddfab717e6a21b52 Mon Sep 17 00:00:00 2001 From: VisualGMQ <2142587070@qq.com> Date: Tue, 2 Aug 2022 01:00:54 +0800 Subject: [PATCH] add start logo; player now flash when hurted --- game/TODO.md | 14 +++++ game/constants.lua | 2 + game/content.lua | 12 ++++- game/ecs.lua | 106 +++++++++++++++++++++++++++---------- game/main.lua | 51 +++++++++++++----- game/resources/License.png | Bin 0 -> 831 bytes game/timer.lua | 59 +++++++++++++++++++++ 7 files changed, 200 insertions(+), 44 deletions(-) create mode 100644 game/TODO.md create mode 100644 game/resources/License.png create mode 100644 game/timer.lua diff --git a/game/TODO.md b/game/TODO.md new file mode 100644 index 0000000..da2c85b --- /dev/null +++ b/game/TODO.md @@ -0,0 +1,14 @@ +[important] +[x] 加上1MGames标志 + +[ ] 角色走路动画 +[ ] 角色受伤特效 +[x] 角色无敌时闪烁 +[ ] 新增多个枪类型 +[ ] 右上角显示分数 +[ ] 玩家积分榜 + +[optional] +[ ] 怪物从土里爬出来(新增怪物类型) +[ ] 掉落宝物(掉落子弹,加分道具) +[ ] 多把武器切换 \ No newline at end of file diff --git a/game/constants.lua b/game/constants.lua index 4a625a3..050257c 100644 --- a/game/constants.lua +++ b/game/constants.lua @@ -1,5 +1,7 @@ local _M = {} +_M.ShowLicenseTime = 2.5 + _M.TileSize = 32 _M.PlayerInfo = { velocity = 250, diff --git a/game/content.lua b/game/content.lua index 5ee6eef..8ff38eb 100644 --- a/game/content.lua +++ b/game/content.lua @@ -10,8 +10,8 @@ _M.RestartHintTexture = nil ---@type Texture _M.StartHintTexture = nil ----@type boolean -_M.IsStartGame = false +---@type Texture +_M.LicensTexture = nil ---@type TileSheet _M.Tilesheet = nil @@ -22,6 +22,14 @@ _M.PlayerEntity = nil ---@type table _M.BulletList = {} +_M.GameStateEnum = { + ShowLogo = 1, + WaitStart = 2, + Gaming = 3, +} + +_M.GameState = _M.GameStateEnum.ShowLogo + ---@type table _M.MonsterList = {} diff --git a/game/ecs.lua b/game/ecs.lua index b3b62f6..df5e811 100644 --- a/game/ecs.lua +++ b/game/ecs.lua @@ -3,6 +3,7 @@ local constants = require "constants" local math = require "math" local vmath = require "vmath" local content = require "content" +local timer = require "timer" ---@class ECS local _M = {} @@ -203,7 +204,6 @@ end ---@class ControllerComponent:Component ---@return ControllerComponent ----@param speed number function _M.CreateControllerComponent() local o = { isActive = true, name = ComponentType.Controller, parent = nil } @@ -229,6 +229,12 @@ function _M.CreateControllerComponent() transform.position.y = transform.position.y + speed * elapseTime end + local canvaSize = hazel.GetCanvaSize() + if transform.position.x < 0 then transform.position.x = 0 end + if transform.position.x + transform.size.x > canvaSize.x then transform.position.x = canvaSize.x - transform.size.x end + if transform.position.y < constants.PlayerHpBarInfo.height then transform.position.y = constants.PlayerHpBarInfo.height end + if transform.position.y + transform.size.y > canvaSize.y then transform.position.y = canvaSize.y - transform.size.y end + ---@type Point local mousePos = hazel.GetGameMousePos() @@ -239,6 +245,14 @@ function _M.CreateControllerComponent() ---@type Point local mouseDir = hazel.CreatePos(mousePos.x - playerCenterX, mousePos.y - playerCenterY) self:GetParent():GetComponent(ComponentType.Direction):SetDir(mouseDir) + + if hazel.GetMouseButtonState(hazel.MouseButton.Left) == hazel.InputState.Press then + ---@type GunComponent + local gun = self:GetParent():GetComponent(ComponentType.Gun) + if gun then + gun:Fire(vmath.Normalize(mouseDir)) + end + end end return _M.CreateComponent(o) @@ -250,26 +264,54 @@ end ---@return InvincibleComponent ---@param time number function _M.CreateInvincibleComponent(time) - local o = { isActive = true, name = ComponentType.Invincible, cooldown = time, timeCountDown = time, parent = nil } + local o = { isActive = true, name = ComponentType.Invincible, isInvincible = false, parent = nil } + o.flashTimer = nil + ---@type Timer + o.cdTimer = timer.CreateTimer(time, 1, function () + o.isInvincible = false + end) + o.cdTimer:Stop() ---@return boolean ---@param self InvincibleComponent o.IsInvincibleState = function(self) - return self.timeCountDown > 0 + return self.cdTimer:IsRunning() end ---@param self InvincibleComponent o.IntoInvincible = function(self) - if self.timeCountDown <= 0 then - self.timeCountDown = self.cooldown + if self.isInvincible then + return end + self.isInvincible = true + self.cdTimer:Rewind() + self.cdTimer:Start() + self.flashTimer = timer.CreateTimer(0.1, time / 0.1, + function() + ---@type ImageComponent + local image = o:GetParent():GetComponent(ComponentType.Image) + if image then + if image:IsActive() then + image:Disable() + else + image:Enable() + end + end + end, + function() + ---@type ImageComponent + local image = o:GetParent():GetComponent(ComponentType.Image) + if image then + image:Enable() + end + end) end ---@param self InvincibleComponent o.Update = function(self) - self.timeCountDown = self.timeCountDown - hazel.Time.GetElapseTime() - if self.timeCountDown < 0 then - self.timeCountDown = 0 + self.cdTimer:Update() + if self.flashTimer then + self.flashTimer:Update() end end @@ -368,34 +410,39 @@ end ---@param damage number ---@param velocity number function _M.CreateGunComponent(damage, velocity) - local o = { isActive = true, name = ComponentType.Gun, damage = damage, timeCounter = 0, parent = nil } + local o = { isActive = true, name = ComponentType.Gun, damage = damage, canShoot = true, velocity = velocity, parent = nil } + o.cdTimer = timer.CreateTimer(constants.GunInfo.cooldown, -1, function() + o.canShoot = true + end) + + ---@param self GunComponent + ---@param dir Point + o.Fire = function(self, dir) + if not self.canShoot then + return + end + ---@type Point + local position = self:GetParent():GetComponent(ComponentType.Transform).position + local playerCenterX = position.x + constants.TileSize / 2 + local playerCenterY = position.y + constants.TileSize / 2 + local ndir = vmath.Normalize(dir) + local bullet = _M.CreateBullet(hazel.CreatePos(playerCenterX - constants.TileSize / 2, playerCenterY - constants.TileSize / 2), + self.damage, + hazel.CreatePos(ndir.x * self.velocity, ndir.y * self.velocity)) + table.insert(content.BulletList, bullet) + + self.canShoot = false + end + ---@param self GunComponent o.Update = function(self) - if self.timeCounter < constants.GunInfo.cooldown then - self.timeCounter = self.timeCounter + hazel.Time.GetElapseTime() - end - if self.timeCounter >= constants.GunInfo.cooldown then - self.timeCounter = constants.GunInfo.cooldown - end - if hazel.GetMouseButtonState(hazel.MouseButton.Left) == hazel.InputState.Press and self.timeCounter >= constants.GunInfo.cooldown then - self.timeCounter = self.timeCounter - constants.GunInfo.cooldown - ---@type Point - local position = self:GetParent():GetComponent(ComponentType.Transform).position - ---@type Point - local mousePos = hazel.GetGameMousePos() - local playerCenterX = position.x + constants.TileSize / 2 - local playerCenterY = position.y + constants.TileSize / 2 - local nMouseDir = vmath.Normalize(hazel.CreatePos(mousePos.x - playerCenterX, mousePos.y - playerCenterY)) - - local bullet = _M.CreateBullet(hazel.CreatePos(playerCenterX - constants.TileSize / 2, playerCenterY - constants.TileSize / 2), self.damage, hazel.CreatePos(nMouseDir.x * velocity, nMouseDir.y * velocity)) - table.insert(content.BulletList, bullet) - end + self.cdTimer:Update() end + return _M.CreateComponent(o) end - ---@class BulletComponent:Component ---@field damage number ---@field speed Point @@ -449,6 +496,7 @@ function _M.CreatePlayer(pos) entity:SetComponent(_M.CreateImageComponent(content.Tilesheet, 0, 0)) entity:SetComponent(_M.CreateRolePropComponent(constants.PlayerInfo.hp, constants.PlayerInfo.velocity)) entity:SetComponent(_M.CreateDirectionComponent(0)) + entity:SetComponent(_M.CreateGunComponent(constants.BulletInfo.damage, constants.BulletInfo.velocity)) entity:SetComponent(_M.CreateColliBoxComponent(constants.RoleColliBox)) entity:SetComponent(_M.CreateInvincibleComponent(constants.Invincible)) return entity diff --git a/game/main.lua b/game/main.lua index 8573271..8b907df 100644 --- a/game/main.lua +++ b/game/main.lua @@ -1,9 +1,9 @@ local hazel = require "hazel" local ECS = require "ecs" local constants = require "constants" -local math = require "math" local content = require "content" local vmath = require "vmath" +local timer = require "timer" local function drawCurosr() hazel.Renderer.SetDrawColor(1, 0, 0, 1) @@ -170,7 +170,7 @@ local function initGame() content.MonsterList = {} content.PlayerEntity = ECS.CreatePlayer(hazel.CreatePos(constants.TileSize * 16, constants.TileSize * 13)) content.MonsterBirthNum = constants.MonsterBirthInitNum - content.IsStartGame = false + content.GameState = content.GameStateEnum.WaitStart end local function generateMonster() @@ -205,15 +205,26 @@ local function generateMonster() content.MonsterBirthCountDown = constants.MonsterBirthInterval end +---@type Timer +local showLicenseTimer = nil + function GameStart() hazel.SetWindowIcon("resources/icon.png") content.Texture = hazel.LoadTexture("resources/tilesheet.png") content.RestartHintTexture = hazel.LoadTexture("resources/RestartHint.png") + content.LicensTexture = hazel.LoadTexture("resources/License.png") content.StartHintTexture = hazel.LoadTexture("resources/StartHint.png") content.Tilesheet = hazel.CreateTileSheet(content.Texture, 3, 10) initGame() + content.GameState = content.GameStateEnum.ShowLogo + + showLicenseTimer = timer.CreateTimer(constants.ShowLicenseTime, 1, function() + content.GameState = content.GameStateEnum.WaitStart + showLicenseTimer = nil + end) + hazel.HideCursor() generateFloors() end @@ -221,14 +232,21 @@ end function GameLoop() hazel.Time.RecordElapseTime() + if showLicenseTimer then + showLicenseTimer:Update() + end + hazel.Renderer.SetClearColor(0, 0, 0, 1) hazel.Renderer.Clear() - drawFloors() - updateMonster() - content.PlayerEntity:Update() - updateBullet() - collisionDeal() + if content.GameState == content.GameStateEnum.ShowLogo then + local dstrect = hazel.CreateRect(0, 0, 0, 0) + dstrect.w = content.LicensTexture.w * 5 + dstrect.h = content.LicensTexture.h * 5 + dstrect.x = (hazel.GetCanvaSize().x - dstrect.w) / 2 + dstrect.y = (hazel.GetCanvaSize().y - dstrect.h) / 2 + hazel.Renderer.DrawTexture(content.LicensTexture, nil, dstrect) + end ---@type RolePropComponent local playerRoleInfo = content.PlayerEntity:GetComponent(ECS.ComponentType.RoleProp) @@ -239,12 +257,20 @@ function GameLoop() end end - if not content.IsStartGame then + if content.GameState == content.GameStateEnum.WaitStart or content.GameState == content.GameStateEnum.Gaming then + drawFloors() + updateMonster() + content.PlayerEntity:Update() + updateBullet() + collisionDeal() + drawCurosr() + end + + if content.GameState == content.GameStateEnum.WaitStart then showStartHint() if hazel.GetMouseButtonState(hazel.MouseButton.Left) == hazel.InputState.Press then - content.IsStartGame = true + content.GameState = content.GameStateEnum.Gaming content.PlayerEntity:SetComponent(ECS.CreateControllerComponent()) - content.PlayerEntity:SetComponent(ECS.CreateGunComponent(constants.BulletInfo.damage, constants.BulletInfo.velocity)) content.PlayerEntity:SetComponent(ECS.CreateHpShowComponent(hazel.CreateSize(constants.PlayerHpBarInfo.width, constants.PlayerHpBarInfo.height))) for _, v in pairs(content.MonsterList) do @@ -253,13 +279,11 @@ function GameLoop() end end - if content.IsStartGame then + if content.GameState == content.GameStateEnum.Gaming then for i = 0, content.MonsterBirthNum do generateMonster() end end - - drawCurosr() end function GameQuit() @@ -267,4 +291,5 @@ function GameQuit() hazel.DestroyTexture(content.Texture) hazel.DestroyTexture(content.RestartHintTexture) hazel.DestroyTexture(content.StartHintTexture) + hazel.DestroyTexture(content.LicensTexture) end diff --git a/game/resources/License.png b/game/resources/License.png new file mode 100644 index 0000000000000000000000000000000000000000..73fe7fff99850bb02d7e63ab0df012957a763fc7 GIT binary patch literal 831 zcmV-F1Hk-=P)Px%_(?=TRA@u(n?aHjF$_iR;s&$g9IQA2XBt)%D>xG;m=)(>#SJJ^YNDX1Wc{B+ zo$3K+(^Ix=%X-$gs*~xh@H563z4snXseSUymXz_Cjn2=^ldZ#!pV?LU!G=E!Dm&QQ z>83m^+s?vdfX{3g|1&>ye3h?^KggyWlxBoIaki~$^Gu*oMj_Buz9I-r|1O|pVV_;s zN*|L^(FRfQZMLBCEGQ)93mPYeYWjogEp-P2|;B+p?xv_Ae#j$iO@BmA{9Zw zzovhIDg@o%Z^z#`e0+cO*4wZAJz{&)dwVxV`yRiK)V^;m zpXWpSGk&y}@V#Yk@5>XQd_r_U1y}@1#(7u*P}>F41|$0jD1i}uv`;|E$HOx10!m@b zmJ>*O069$6Hv=V*Y-Z`-`YZ({kZfk@->2sSwaG=CQJPIQi#B;VNgoX=%Q!E6KD!L4 zXd0hok@W4AWrvYIbVqqm{=PcjQH=BvcNCJoM@!^{;GAdd906_&#XT(U$&k)V8L zSp~6uHmhtqC4Dv^_duGRz*~SsGGS$rfq*2oGNZg?dtWnfVtZ9a*{s6X-`v6mfRQP7 zEx*}lT$RmCA^ZVi@2CaJ(b^kSbjLD>apu=ecT`nI*$OASh#Bd*H!!zmU3TaCzA{jm zv_$`zQSrkR_8&F#rGn07*qo JM6N<$f&hXqjPU>f literal 0 HcmV?d00001 diff --git a/game/timer.lua b/game/timer.lua new file mode 100644 index 0000000..8e35bd3 --- /dev/null +++ b/game/timer.lua @@ -0,0 +1,59 @@ +local hazel = require "hazel" + +---@class Timer +local _M = {} + +---@param interval number seconds +---@param loop number -1 means infinite loop +---@param callback function|nil a no param, no return value callback function +---@param onStopFunc function|nil a no param, no return value callback function called when timer stop +function _M.CreateTimer(interval, loop, callback, onStopFunc) + local o = {isStart = true, interval = interval, totleLoop = loop, loop = loop, callback = callback, onStopFunc = onStopFunc, counter = 0} + setmetatable(o, {__index = _M}) + return o +end + +---@param self Timer +function _M.Rewind(self) + self.loop = self.totleLoop +end + +---@param self Timer +function _M.Start(self) + self.isStart = true +end + +---@return boolean +---@param self Timer +function _M.IsRunning(self) + return self.isStart +end + +---@param self Timer +function _M.Stop(self) + self.isStart = false +end + +---@param self Timer +function _M.Update(self) + if self.loop == 0 then + if self:IsRunning() then + if self.onStopFunc then + self.onStopFunc() + end + self:Stop() + end + end + if self:IsRunning() then + self.counter = self.counter + hazel.Time.GetElapseTime() + while self.counter >= self.interval do + self.counter = self.counter - self.interval + self.loop = self.loop - 1 + if self.callback then + self.callback() + end + end + end +end + +return _M \ No newline at end of file