add start logo; player now flash when hurted

This commit is contained in:
VisualGMQ 2022-08-02 01:00:54 +08:00
parent 5504ce68d2
commit a86f0719fb
7 changed files with 200 additions and 44 deletions

14
game/TODO.md Normal file
View File

@ -0,0 +1,14 @@
[important]
[x] 加上1MGames标志
[ ] 角色走路动画
[ ] 角色受伤特效
[x] 角色无敌时闪烁
[ ] 新增多个枪类型
[ ] 右上角显示分数
[ ] 玩家积分榜
[optional]
[ ] 怪物从土里爬出来(新增怪物类型)
[ ] 掉落宝物(掉落子弹,加分道具)
[ ] 多把武器切换

View File

@ -1,5 +1,7 @@
local _M = {} local _M = {}
_M.ShowLicenseTime = 2.5
_M.TileSize = 32 _M.TileSize = 32
_M.PlayerInfo = { _M.PlayerInfo = {
velocity = 250, velocity = 250,

View File

@ -10,8 +10,8 @@ _M.RestartHintTexture = nil
---@type Texture ---@type Texture
_M.StartHintTexture = nil _M.StartHintTexture = nil
---@type boolean ---@type Texture
_M.IsStartGame = false _M.LicensTexture = nil
---@type TileSheet ---@type TileSheet
_M.Tilesheet = nil _M.Tilesheet = nil
@ -22,6 +22,14 @@ _M.PlayerEntity = nil
---@type table<Entity> ---@type table<Entity>
_M.BulletList = {} _M.BulletList = {}
_M.GameStateEnum = {
ShowLogo = 1,
WaitStart = 2,
Gaming = 3,
}
_M.GameState = _M.GameStateEnum.ShowLogo
---@type table<Entity> ---@type table<Entity>
_M.MonsterList = {} _M.MonsterList = {}

View File

@ -3,6 +3,7 @@ local constants = require "constants"
local math = require "math" local math = require "math"
local vmath = require "vmath" local vmath = require "vmath"
local content = require "content" local content = require "content"
local timer = require "timer"
---@class ECS ---@class ECS
local _M = {} local _M = {}
@ -203,7 +204,6 @@ end
---@class ControllerComponent:Component ---@class ControllerComponent:Component
---@return ControllerComponent ---@return ControllerComponent
---@param speed number
function _M.CreateControllerComponent() function _M.CreateControllerComponent()
local o = { isActive = true, name = ComponentType.Controller, parent = nil } local o = { isActive = true, name = ComponentType.Controller, parent = nil }
@ -229,6 +229,12 @@ function _M.CreateControllerComponent()
transform.position.y = transform.position.y + speed * elapseTime transform.position.y = transform.position.y + speed * elapseTime
end 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 ---@type Point
local mousePos = hazel.GetGameMousePos() local mousePos = hazel.GetGameMousePos()
@ -239,6 +245,14 @@ function _M.CreateControllerComponent()
---@type Point ---@type Point
local mouseDir = hazel.CreatePos(mousePos.x - playerCenterX, mousePos.y - playerCenterY) local mouseDir = hazel.CreatePos(mousePos.x - playerCenterX, mousePos.y - playerCenterY)
self:GetParent():GetComponent(ComponentType.Direction):SetDir(mouseDir) 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 end
return _M.CreateComponent(o) return _M.CreateComponent(o)
@ -250,26 +264,54 @@ end
---@return InvincibleComponent ---@return InvincibleComponent
---@param time number ---@param time number
function _M.CreateInvincibleComponent(time) 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 ---@return boolean
---@param self InvincibleComponent ---@param self InvincibleComponent
o.IsInvincibleState = function(self) o.IsInvincibleState = function(self)
return self.timeCountDown > 0 return self.cdTimer:IsRunning()
end end
---@param self InvincibleComponent ---@param self InvincibleComponent
o.IntoInvincible = function(self) o.IntoInvincible = function(self)
if self.timeCountDown <= 0 then if self.isInvincible then
self.timeCountDown = self.cooldown return
end 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 end
---@param self InvincibleComponent ---@param self InvincibleComponent
o.Update = function(self) o.Update = function(self)
self.timeCountDown = self.timeCountDown - hazel.Time.GetElapseTime() self.cdTimer:Update()
if self.timeCountDown < 0 then if self.flashTimer then
self.timeCountDown = 0 self.flashTimer:Update()
end end
end end
@ -368,34 +410,39 @@ end
---@param damage number ---@param damage number
---@param velocity number ---@param velocity number
function _M.CreateGunComponent(damage, velocity) 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 ---@param self GunComponent
o.Update = function(self) o.Update = function(self)
if self.timeCounter < constants.GunInfo.cooldown then self.cdTimer:Update()
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
end end
return _M.CreateComponent(o) return _M.CreateComponent(o)
end end
---@class BulletComponent:Component ---@class BulletComponent:Component
---@field damage number ---@field damage number
---@field speed Point ---@field speed Point
@ -449,6 +496,7 @@ function _M.CreatePlayer(pos)
entity:SetComponent(_M.CreateImageComponent(content.Tilesheet, 0, 0)) entity:SetComponent(_M.CreateImageComponent(content.Tilesheet, 0, 0))
entity:SetComponent(_M.CreateRolePropComponent(constants.PlayerInfo.hp, constants.PlayerInfo.velocity)) entity:SetComponent(_M.CreateRolePropComponent(constants.PlayerInfo.hp, constants.PlayerInfo.velocity))
entity:SetComponent(_M.CreateDirectionComponent(0)) entity:SetComponent(_M.CreateDirectionComponent(0))
entity:SetComponent(_M.CreateGunComponent(constants.BulletInfo.damage, constants.BulletInfo.velocity))
entity:SetComponent(_M.CreateColliBoxComponent(constants.RoleColliBox)) entity:SetComponent(_M.CreateColliBoxComponent(constants.RoleColliBox))
entity:SetComponent(_M.CreateInvincibleComponent(constants.Invincible)) entity:SetComponent(_M.CreateInvincibleComponent(constants.Invincible))
return entity return entity

View File

@ -1,9 +1,9 @@
local hazel = require "hazel" local hazel = require "hazel"
local ECS = require "ecs" local ECS = require "ecs"
local constants = require "constants" local constants = require "constants"
local math = require "math"
local content = require "content" local content = require "content"
local vmath = require "vmath" local vmath = require "vmath"
local timer = require "timer"
local function drawCurosr() local function drawCurosr()
hazel.Renderer.SetDrawColor(1, 0, 0, 1) hazel.Renderer.SetDrawColor(1, 0, 0, 1)
@ -170,7 +170,7 @@ local function initGame()
content.MonsterList = {} content.MonsterList = {}
content.PlayerEntity = ECS.CreatePlayer(hazel.CreatePos(constants.TileSize * 16, constants.TileSize * 13)) content.PlayerEntity = ECS.CreatePlayer(hazel.CreatePos(constants.TileSize * 16, constants.TileSize * 13))
content.MonsterBirthNum = constants.MonsterBirthInitNum content.MonsterBirthNum = constants.MonsterBirthInitNum
content.IsStartGame = false content.GameState = content.GameStateEnum.WaitStart
end end
local function generateMonster() local function generateMonster()
@ -205,15 +205,26 @@ local function generateMonster()
content.MonsterBirthCountDown = constants.MonsterBirthInterval content.MonsterBirthCountDown = constants.MonsterBirthInterval
end end
---@type Timer
local showLicenseTimer = nil
function GameStart() function GameStart()
hazel.SetWindowIcon("resources/icon.png") hazel.SetWindowIcon("resources/icon.png")
content.Texture = hazel.LoadTexture("resources/tilesheet.png") content.Texture = hazel.LoadTexture("resources/tilesheet.png")
content.RestartHintTexture = hazel.LoadTexture("resources/RestartHint.png") content.RestartHintTexture = hazel.LoadTexture("resources/RestartHint.png")
content.LicensTexture = hazel.LoadTexture("resources/License.png")
content.StartHintTexture = hazel.LoadTexture("resources/StartHint.png") content.StartHintTexture = hazel.LoadTexture("resources/StartHint.png")
content.Tilesheet = hazel.CreateTileSheet(content.Texture, 3, 10) content.Tilesheet = hazel.CreateTileSheet(content.Texture, 3, 10)
initGame() initGame()
content.GameState = content.GameStateEnum.ShowLogo
showLicenseTimer = timer.CreateTimer(constants.ShowLicenseTime, 1, function()
content.GameState = content.GameStateEnum.WaitStart
showLicenseTimer = nil
end)
hazel.HideCursor() hazel.HideCursor()
generateFloors() generateFloors()
end end
@ -221,14 +232,21 @@ end
function GameLoop() function GameLoop()
hazel.Time.RecordElapseTime() hazel.Time.RecordElapseTime()
if showLicenseTimer then
showLicenseTimer:Update()
end
hazel.Renderer.SetClearColor(0, 0, 0, 1) hazel.Renderer.SetClearColor(0, 0, 0, 1)
hazel.Renderer.Clear() hazel.Renderer.Clear()
drawFloors() if content.GameState == content.GameStateEnum.ShowLogo then
updateMonster() local dstrect = hazel.CreateRect(0, 0, 0, 0)
content.PlayerEntity:Update() dstrect.w = content.LicensTexture.w * 5
updateBullet() dstrect.h = content.LicensTexture.h * 5
collisionDeal() 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 ---@type RolePropComponent
local playerRoleInfo = content.PlayerEntity:GetComponent(ECS.ComponentType.RoleProp) local playerRoleInfo = content.PlayerEntity:GetComponent(ECS.ComponentType.RoleProp)
@ -239,12 +257,20 @@ function GameLoop()
end end
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() showStartHint()
if hazel.GetMouseButtonState(hazel.MouseButton.Left) == hazel.InputState.Press then 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.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))) content.PlayerEntity:SetComponent(ECS.CreateHpShowComponent(hazel.CreateSize(constants.PlayerHpBarInfo.width, constants.PlayerHpBarInfo.height)))
for _, v in pairs(content.MonsterList) do for _, v in pairs(content.MonsterList) do
@ -253,13 +279,11 @@ function GameLoop()
end end
end end
if content.IsStartGame then if content.GameState == content.GameStateEnum.Gaming then
for i = 0, content.MonsterBirthNum do for i = 0, content.MonsterBirthNum do
generateMonster() generateMonster()
end end
end end
drawCurosr()
end end
function GameQuit() function GameQuit()
@ -267,4 +291,5 @@ function GameQuit()
hazel.DestroyTexture(content.Texture) hazel.DestroyTexture(content.Texture)
hazel.DestroyTexture(content.RestartHintTexture) hazel.DestroyTexture(content.RestartHintTexture)
hazel.DestroyTexture(content.StartHintTexture) hazel.DestroyTexture(content.StartHintTexture)
hazel.DestroyTexture(content.LicensTexture)
end end

BIN
game/resources/License.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 B

59
game/timer.lua Normal file
View File

@ -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