Compare commits

..

6 Commits
v0.2.0 ... main

Author SHA1 Message Date
VisualGMQ 5d2f9c8541 fixed readme 2022-08-06 03:04:08 +08:00
VisualGMQ 4424175628 fixed version 2022-08-06 02:59:53 +08:00
VisualGMQ 530f347b7b add slow trap 2022-08-06 02:31:13 +08:00
VisualGMQ 21740b8349 add some hint icon 2022-08-06 00:32:20 +08:00
VisualGMQ de703adedf let clang & msvc compile success 2022-08-05 23:15:31 +08:00
VisualGMQ f963b514e5 fixed readmd 2022-08-04 00:33:36 +08:00
13 changed files with 218 additions and 26 deletions

View File

@ -38,6 +38,7 @@ add_subdirectory(test)
# install
install(
TARGETS ${HAZEL_LUA_BRIDGE_NAME} lua ${HAZEL_EXECUTOR_NAME} ${HAZEL_CORE_NAME}
RUNTIME
DESTINATION ${CMAKE_SOURCE_DIR}/install
)

View File

@ -1,3 +1,38 @@
A tiny engine made with C and Lua for 1MGames
为了1MGames创建的陪跑游戏。游戏采用主题**“无限”**。玩家扮演一个枪手,尽可能地杀光袭来的外星生物。
NOTE: **Under Windows, you must use MinGW to compile**
# 下载
Github可以直接在Release界面找到Windows的可执行压缩包。
Gitme可以在`版本发布`下找到同样的压缩包。
# 编译
本仓库的本体是一个基于C和Lua的游戏框架TinyHazelEngine所以当你使用cmake编译后并不会得到游戏本身而是得到这个游戏框架。
本仓库目前只能在Windows下编译成功因为我懒得写音频处理直接用了Windows的API
拉取工程后首先拉取其子工程
```bash
git submodule update --init --recursive
```
然后使用cmake编译(**如果你使用MSVC和Clang编译需要将最后的hazel.dll重命名为libhazel.dll。如果是MinGW那么他应当产出libhazel.dll**)
```bash
cmake -S . -B build
```
然后安装到`install`文件夹:
```bash
cmake --build build --target install
```
游戏本体在`game`文件夹下,所以你需要将`install`文件夹内的所有`.dll`文件,`shader`文件夹和`HazelRunner.exe`拷贝到`game`文件夹下。
然后双击`game/HazelRunner.exe`就可以打开游戏了。
# 游戏截图
![snapshot](./snapshot/snapshot.png)

View File

@ -39,6 +39,6 @@ target_compile_features(lua PRIVATE c_std_99)
# TODO if you use MinGW, maybe you need this
# target_compile_options(lua PRIVATE LUA_USE_LINUX)
if (NOT WIN32)
target_compile_options(lua PRIVATE LUA_USE_LINUX)
if (WIN32)
target_compile_definitions(lua PRIVATE "LUA_BUILD_AS_DLL")
endif()

25
game/HowToPlay.txt Normal file
View File

@ -0,0 +1,25 @@
# InfinitShoot
## 游戏介绍
玩家扮演一个枪手,要尽可能地在怪物的围攻中活下来
## 游戏玩法
左键射击wasd移动
## 游戏内容
游戏中的敌人会向你移动,想办法击杀或者躲开它们。
游戏中存在三种类型的子弹:
* 普通子弹:玩家初始的子弹,射速最快,伤害中等,但是没有特殊效果
* 火焰子弹:射速最慢,但是伤害最高,附加怪物火焰效果
* 冰冻子弹:射速中等,伤害中等,可以冻住怪物,冰冻期间触碰怪物不会受到伤害
特殊效果子弹通过击杀20个敌人后从空投中获得。
空投中除了子弹,还有回血的道具,吃完后立刻回血。
游戏中的敌人尸体在一定时间后会腐烂,踩在腐烂的尸体上将会极大地减少玩家的速度

View File

@ -1,4 +1,4 @@
WindowWidth = 1024
WindowHeight = 720
Title = "InfiniteShoot - VisualGMQ For 1MGames v0.1.0"
Title = "InfiniteShoot - VisualGMQ For 1MGames v1.0.0"
EntryFile = "main.lua"

View File

@ -7,18 +7,23 @@ _M.SoundName = {
PlayerHurt = "PLAYER_HURT",
MonsterHurt = "MONSTER_HURT",
GameOver = "GAMEOVER",
HpRecover = "HPRECOVER",
PickupGun = "PickupGun",
}
_M.TileSize = 32
_M.PlayerInfo = {
velocity = 250,
hp = 100,
velocity = 200,
hp = 50,
}
_M.PlayerSlowSpeed = 50
_M.MonsterInfo = {
velocity = 100,
hp = 50,
damage = 10,
}
_M.MonsterDissolveTime = 5
_M.RoleColliBox = {
w = 14,
h = 32,
@ -49,9 +54,6 @@ _M.MonsetHpBarInfo= {
width = _M.TileSize,
height = 5,
}
_M.GunInfo = {
cooldown = 0.1
}
_M.BulletType = {
Normal = 1,
@ -69,20 +71,20 @@ _M.BulletInfo = {
[_M.BulletType.Ice] = {
damage = 5,
velocity = 400,
cooldown = 0.1,
cooldown = 0.2,
initNum = 100,
},
[_M.BulletType.Fire] = {
damage = 7,
velocity = 600,
cooldown = 0.1,
cooldown = 0.4,
fireDamage = 1,
initNum = 100,
},
}
_M.BulletEffectTime = {
IceTime = 2,
IceTime = 5,
FireTime = 2,
}
_M.RoleState = {
@ -90,7 +92,7 @@ _M.RoleState = {
Ice = 2,
Fire = 3,
}
_M.SupplyFalldownKillNum = 120
_M.SupplyFalldownKillNum = 20
_M.SupplyType = {
IceGun = 1,
@ -98,8 +100,8 @@ _M.SupplyType = {
HpRecover = 3,
}
_M.SupplyItem = {
[_M.SupplyType.IceGun] = { num = 100 },
[_M.SupplyType.FireGun] = { num = 100 },
[_M.SupplyType.IceGun] = { num = 50},
[_M.SupplyType.FireGun] = { num = 50},
[_M.SupplyType.HpRecover] = { recover = 50},
}

View File

@ -34,11 +34,16 @@ _M.GameStateEnum = {
Gaming = 3,
}
_M.HpRecoverListAnim = {}
_M.GameState = _M.GameStateEnum.ShowLogo
---@type table<Entity>
_M.MonsterList = {}
---@type table<Entity>
_M.MonsterCorpseList = {}
_M.MonsterBirthNum = 0
_M.MonsterBirthCountDown = 0

View File

@ -117,7 +117,8 @@ local ComponentType = {
Animator = 11,
State = 12,
Supply = 13,
Image = 14,
SlowTrap = 14,
Image = 15,
}
_M.ComponentType = ComponentType
@ -430,6 +431,9 @@ function _M.CreateGunComponent(type, bulletNum)
o.SetType = function(self, type)
self.type = type or constants.BulletType.Normal
self.bulletNum = constants.BulletInfo[self.type].initNum
o.cdTimer = timer.CreateTimer(constants.BulletInfo[type].cooldown, -1, function()
o.canShoot = true
end)
end
o.GetBulletNum = function(self)
@ -521,6 +525,36 @@ function _M.CreateSupplyComponent(type)
end
---@class SlowTrapComponent:Component
---@field IsEnable function
---@field StartDissolve function
---@return SupplyComponent
function _M.CreateSlowTrapComponent()
local o = { isActive = true, name = ComponentType.SlowTrap, isEnable = false, parent = nil }
---@type Timer
o.timer = nil
o.StartDissolve = function(self)
self.timer = timer.CreateTimer(constants.MonsterDissolveTime, 1, function()
self.isEnable = true
---@type ImageComponent
local image = self:GetParent():GetComponent(ComponentType.Image)
if image then
image.row = 12
image.col = 0
end
end)
end
o.IsEnable = function(self) return self.isEnable end
o.Update = function(self)
if self.timer then
self.timer:Update()
end
end
return _M.CreateComponent(o)
end
---@class StateComponent:Component
---@field rect Rect
@ -575,6 +609,9 @@ function _M.CreateStateComponent(state)
local oldHp = roleProp.hp
roleProp.hp = roleProp.hp - constants.BulletInfo[constants.BulletType.Fire].fireDamage
if oldHp > 0 and roleProp.hp <= 0 then
local slowTrap = _M.CreateSlowTrapComponent()
self:GetParent():SetComponent(slowTrap)
slowTrap:StartDissolve()
content.Score = content.Score + 1
helpfuncs.IncKillNum()
end
@ -634,7 +671,6 @@ function _M.CreateAnimatorComponent(ani)
return _M.CreateComponent(o)
end
---@return Entity
---@param pos Point
---@param damage number
@ -665,7 +701,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.BulletType.Fire))
entity:SetComponent(_M.CreateGunComponent(constants.BulletType.Normal))
entity:SetComponent(_M.CreateColliBoxComponent(constants.RoleColliBox))
entity:SetComponent(_M.CreateInvincibleComponent(constants.Invincible))
entity:SetComponent(_M.CreateStateComponent())
@ -699,5 +735,4 @@ function _M.CreateSupply(type, pos)
return entity
end
return _M

View File

@ -66,7 +66,19 @@ end
local function updateMonster()
---@param v Entity
for _, v in pairs(content.MonsterList) do
for i, v in pairs(content.MonsterList) do
if v:GetComponent(ECS.ComponentType.RoleProp):IsDie() then
table.insert(content.MonsterCorpseList, v)
content.MonsterList[i] = nil
else
v:Update()
end
end
end
local function updateMonsterCropse()
---@param v Entity
for _, v in pairs(content.MonsterCorpseList) do
v:Update()
end
end
@ -85,7 +97,7 @@ local function collisionDeal()
local playerPos = content.PlayerEntity:GetComponent(ECS.ComponentType.Transform).position
---@type Rect
local playerColliBox = hazel.CreateRect(playerPos.x + playerBox.x, playerPos.y + playerBox.y, playerBox.w, playerBox.h)
---@type m Entity
---@type Entity
for km, _ in pairs(content.MonsterList) do
---@type Entity
local monster = content.MonsterList[km]
@ -101,7 +113,10 @@ local function collisionDeal()
monsterBox.w, monsterBox.h)
---@type RolePropComponent
local monsterRoleProp = monster:GetComponent(ECS.ComponentType.RoleProp)
if vmath.IsRectIntersect(playerColliBox, monsterColliBox) then
---@type StateComponent
local mosnterState = monster:GetComponent(ECS.ComponentType.State):GetState()
if mosnterState ~= constants.RoleState.Ice and vmath.IsRectIntersect(playerColliBox, monsterColliBox) then
---@type InvincibleComponent
local playerInvincible = content.PlayerEntity:GetComponent(ECS.ComponentType.Invincible)
if playerInvincible and not playerInvincible:IsInvincibleState() then
@ -152,6 +167,10 @@ local function collisionDeal()
hazel.Sound.Play(constants.SoundName.MonsterHurt)
if monsterRoleProp:IsDie() then
if oldHp > 0 then
---@type SlowTrapComponent
local slowTrap = ECS.CreateSlowTrapComponent()
monster:SetComponent(slowTrap)
slowTrap:StartDissolve()
content.Score = content.Score + 1
helpfunc.IncKillNum()
end
@ -173,8 +192,16 @@ local function collisionDeal()
---@type GunComponent
local gun = content.PlayerEntity:GetComponent(ECS.ComponentType.Gun)
local type = v:GetComponent(ECS.ComponentType.Supply).type
print(type)
if type == constants.SupplyType.HpRecover then
local index = #content.HpRecoverListAnim + 1
local hpRecoverAnim = animation.CreateAnimation(content.Tilesheet, {
{row = 12, col = 1, time = 0.5},
}, function()
content.HpRecoverListAnim[index] = nil
end)
hpRecoverAnim:Play()
table.insert(content.HpRecoverListAnim, index, hpRecoverAnim)
hazel.Sound.Play(constants.SoundName.HpRecover)
---@type RolePropComponent
local roleProp = content.PlayerEntity:GetComponent(ECS.ComponentType.RoleProp)
roleProp.hp = roleProp.hp + constants.SupplyItem[type].recover
@ -184,13 +211,42 @@ local function collisionDeal()
elseif type == constants.SupplyType.IceGun then
gun:SetType(constants.BulletType.Ice)
gun.bulletNum = gun.bulletNum + constants.SupplyItem[type].num
hazel.Sound.Play(constants.SoundName.PickupGun)
elseif type == constants.SupplyType.FireGun then
gun:SetType(constants.BulletType.Fire)
gun.bulletNum = gun.bulletNum + constants.SupplyItem[type].num
hazel.Sound.Play(constants.SoundName.PickupGun)
end
end
end
end
---@type boolean
local isSlowed = False
---@type RolePropComponent
local playerProp = content.PlayerEntity:GetComponent(ECS.ComponentType.RoleProp)
playerProp.speed = constants.PlayerSlowSpeed
---@param mk number
---@param corpse Entity
for mk, corpse in pairs(content.MonsterCorpseList) do
---@type Point
local corpsePos = corpse:GetComponent(ECS.ComponentType.Transform).position
---@type Rect
local corpseBox = corpse:GetComponent(ECS.ComponentType.ColliBox).rect
---@type Rect
local corpseColliBox = hazel.CreateRect(corpsePos.x + corpseBox.x, corpsePos.y + corpseBox.y,
corpseBox.w, corpseBox.h)
---@type SlowTrapComponent
local slowTrap = corpse:GetComponent(ECS.ComponentType.SlowTrap)
if slowTrap:IsEnable() and vmath.IsRectIntersect(corpseColliBox, playerColliBox) then
isSlowed = true
playerProp.speed = constants.PlayerSlowSpeed
end
end
if not isSlowed then
playerProp.speed = constants.PlayerInfo.velocity
end
end
local function showRestartHint()
@ -218,8 +274,10 @@ end
local function initGame()
content.KillNum = 0
content.Score = 0
content.HpRecoverListAnim = {}
content.BulletList = {}
content.MonsterList = {}
content.MonsterCorpseList = {}
content.SupplyList = {}
content.PlayerEntity = ECS.CreatePlayer(hazel.CreatePos(constants.TileSize * 16, constants.TileSize * 13))
content.MonsterBirthNum = constants.MonsterBirthInitNum
@ -297,6 +355,23 @@ local function drawNum(num, x, y)
end
end
local function updateHpRecoverAnim()
for _, ani in pairs(content.HpRecoverListAnim) do
if ani:IsPlaying() then
ani:Update()
---@type TileSheet
local tilesheet = ani:GetTilesheet()
---@type Frame
local frame = ani:GetCurFrame()
---@type Point
local pos = content.PlayerEntity:GetComponent(ECS.ComponentType.Transform).position
tilesheet:Draw(frame.col, frame.row,
hazel.CreateRect(pos.x, pos.y - constants.TileSize - 5,
constants.TileSize, constants.TileSize))
end
end
end
local function updateRoles()
---@param monster Entity
for _, monster in pairs(content.MonsterList) do
@ -306,7 +381,7 @@ local function updateRoles()
monster:RemoveComponent(ECS.ComponentType.Direction)
monster:RemoveComponent(ECS.ComponentType.AI)
monster:RemoveComponent(ECS.ComponentType.HpShow)
monster:RemoveComponent(ECS.ComponentType.ColliBox)
-- monster:RemoveComponent(ECS.ComponentType.ColliBox)
monster:RemoveComponent(ECS.ComponentType.State)
---@type ImageComponent
local image = monster:GetComponent(ECS.ComponentType.Image)
@ -322,13 +397,15 @@ function GameStart()
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, 12)
content.Tilesheet = hazel.CreateTileSheet(content.Texture, 3, 13)
content.NumberTexture = hazel.LoadTexture("resources/numbers.png")
content.NumberTilesheet = hazel.CreateTileSheet(content.NumberTexture, 11, 1)
hazel.Sound.Load("resources/gameover.wav", constants.SoundName.GameOver)
hazel.Sound.Load("resources/player_hurt.wav", constants.SoundName.PlayerHurt)
hazel.Sound.Load("resources/monster_hurt.wav", constants.SoundName.MonsterHurt)
hazel.Sound.Load("resources/shoot.wav", constants.SoundName.Shoot)
hazel.Sound.Load("resources/hprecover.wav", constants.SoundName.HpRecover)
hazel.Sound.Load("resources/pickupgun.wav", constants.SoundName.PickupGun)
initGame()
@ -373,6 +450,7 @@ function GameLoop()
if content.GameState == content.GameStateEnum.WaitStart or content.GameState == content.GameStateEnum.Gaming then
drawFloors()
updateMonsterCropse()
updateSupply()
updateMonster()
content.PlayerEntity:Update()
@ -380,6 +458,7 @@ function GameLoop()
collisionDeal()
drawCurosr()
updateRoles()
updateHpRecoverAnim()
end
---@type RolePropComponent
@ -395,8 +474,18 @@ function GameLoop()
---@type GunComponent
local gun = content.PlayerEntity:GetComponent(ECS.ComponentType.Gun)
local x = hazel.GetCanvaSize().x - 128
content.Tilesheet:Draw(2, 11, hazel.CreateRect(x - 64, 22, 50, 50))
drawNum(content.Score, x, 32)
if gun then
---@param type number
local type = gun.type
if type == constants.BulletType.Fire then
content.Tilesheet:Draw(1, 10, hazel.CreateRect(x - 64, 70, 50, 50))
elseif type == constants.BulletType.Ice then
content.Tilesheet:Draw(0, 10, hazel.CreateRect(x - 64, 80, 50, 50))
else
content.Tilesheet:Draw(1, 4, hazel.CreateRect(x - 64, 80, 50, 50))
end
drawNum(gun:GetBulletNum(), x, 80)
end
end

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
snapshot/snapshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB