使用 Cosos2dX-Lua 制作一个横版过关游戏 (3)
来源:互联网 发布:js 判断zepto 编辑:程序博客网 时间:2024/04/28 10:13
使用 Cosos2dX-Lua 制作一个横版过关游戏 (3)
Written by dreamfairy on 2013 年 10 月 09 日. Posted in C++, Cocos2d-x, Lua
总之,运行后就是这样
1.创建Robot类
2.添加 Hero 和 Robot 受伤 和 死亡 动作
3.添加 Robot 的AI 使之会跟踪玩家并攻击
4.添加音效
在 ActionSprite.lua 中添加方法
2
3
4
5
6
7
8
9
10
11
12
local boundingBox = {}
boundingBox.original = CCRect()
boundingBox.actual = CCRect()
boundingBox.original.origin = origin
boundingBox.original.size = size
boundingBox.actual.origin = ccpAdd(ccp(self:getPositionX(),self:getPositionY()),ccp(boundingBox.original.origin.x,boundingBox.original.origin.y))
boundingBox.actual.size = size
return boundingBox
end
这里创建一个包围盒,其中 original 为 角色原始矩形
actual 为真实矩形,它会根据角色的坐标,不断修改该矩形的 x,y, 这个函数只在初始化时调用
继续添加一个函数,这个函数将在修改角色坐标时调用,不断修改 actual 矩形
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
local hitBox = self:getHitBox()
local attackBox = self:getAttackBox()
local originX
if self:getScaleX() == -1 then
originX = - attackBox.original.size.width - hitBox.original.size.width
else
originX = 0
end
local position = ccp(self:getPositionX(),self:getPositionY())
hitBox.actual.origin = ccpAdd(position, ccp(hitBox.original.origin.x, hitBox.original.origin.y));
attackBox.actual.origin = ccpAdd(position, ccp(attackBox.original.origin.x + originX, attackBox.original.origin.y));
self:setHitBox(hitBox)
self:setAttackBox(attackBox)
end
添加函数 setPositions() 调用此函数来替换之前 在 GameLayer.lua 中 诸如 Hero:setPosition() 替换为 Hero.getClass().setPosition(Hero,x,y)
2
3
4
self:setPosition(posX,posY)
ActionSprite.transformBoxes(self)
end
这个函数在执行的时候,会修改玩家角色的坐标,然后更新该角色的actual 矩形
这个矩形将被用到碰撞检测,判断攻击是否成功
添加函数 hurtWithDamage(damage),knockout(), 外部传入的伤害会由角色的HurtPoint 抵消,当 HurtPoint 为0后,调用死亡动画
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
local ActionState = self:getActionState()
local HurtPoint = self:getHurtPoint()
if ActionState ~= ACTION_STATE_KNOCKOUT then
self:stopAllActions()
self:createHurtAction();
self:runAction(self:getHurtAction())
ActionState = ACTION_STATE_HURT
self:setActionState(ActionSprite)
HurtPoint = HurtPoint - damage
self:setHurtPoint(HurtPoint)
if HurtPoint <= 0 then
ActionSprite.knockout(self)
end
end
end
function ActionSprite:knockout()
self:stopAllActions()
self:createKnockOutAction()
self:runAction(self:getKnockOutAction())
self:setActionState(ACTION_STATE_KNOCKOUT)
self:playDeathSound()
end
创建 Robot.lua 包路径 scenes.GameObjects.Robot.lua
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
return display.newSprite("#robot_idle_00.png")
end)
require("scenes.Define")
require("config")
local Prototype = require("scenes.GameObjects.ActionSprite"):extend(Robot)
--Animation Cache
local ROBOT_IDLE = "RobotIdle"
local ROBOT_ATTACK = "RobotAttack"
local ROBOT_WALK = "RobotWalk"
local ROBOT_HURT = "RobotHurt"
local ROBOT_KNOCKOUT = "RobotKnockOut"
--attribute
local WalkSpeed
local Damage
--measurements
local CenterToSide
local CenterToBottom
function Robot:ctor(name)
WalkSpeed = 80
self.HurtPoint = 100
Damage = 10
CenterToSide = 29
CenterToBottom = 39
self.ActionState = ACTION_STATE_NONE
self.Name = name
self.NextDecisionTime = 0
self.HitBox = Prototype.createBoundingBoxWithOrigin(self,ccp(-self:getCenterToSides(), -self:getCenterToBottom()),
CCSizeMake(self:getCenterToSides() * 2, self:getCenterToBottom() * 2))
self.AttackBox = Prototype.createBoundingBoxWithOrigin(self,ccp(self:getCenterToSides(), -5), CCSizeMake(25, 20))
end
在构造函数 ctor() 中,初始化了2个矩形 HitBox 和 AttackBox.
HitBox 为当角色属于被击中的一方时,将此矩形用于碰撞检测
AttackBox 为当角色属于攻击的一方时,将此矩形用于碰撞检测
juse like this, 攻击时的矩形在手部,其余部分为被攻击矩形
继续填充 Robot.lua 的代码 添加 受伤 和 死亡的动作 以及一些 getter,setter
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
return Prototype
end
function Robot:getName()
return self.Name
end
function Robot:update(dt)
Prototype.update(self,dt)
end
function Robot:createIdleAction()
local frames = display.newFrames("robot_idle_%02d.png",0,5)
local animation = display.newAnimation(frames,1/12)
display.setAnimationCache(ROBOT_IDLE,animation)
self.IdleAction = CCRepeatForever:create(CCAnimate:create(animation))
end
function Robot:createAttackAction()
local frames = display.newFrames("robot_attack_%02d.png",0,5)
local animation = display.newAnimation(frames,1/24)
display.setAnimationCache(ROBOT_ATTACK,animation)
local idelFunc = CCCallFunc:create(function() self:idle() end)
self.AttackAction = CCSequence:createWithTwoActions(CCAnimate:create(animation),idelFunc)
end
function Robot:createWalkAction()
local frames = display.newFrames("robot_walk_%02d.png",0,6)
local animation = display.newAnimation(frames,1/12)
display.setAnimationCache(ROBOT_WALK,animation)
local idelFunc = CCCallFunc:create(function() self:idle() end)
self.WalkAction = CCRepeatForever:create(CCAnimate:create(animation))
end
function Robot:createHurtAction()
local frames = display.newFrames("robot_hurt_%02d.png",0,3)
local animation = display.newAnimation(frames,1/12)
display.setAnimationCache(ROBOT_HURT,animation)
local idelFunc = CCCallFunc:create(function() self:idle() end)
self.HurtAction = CCSequence:createWithTwoActions(CCAnimate:create(animation),idelFunc)
end
function Robot:createKnockOutAction()
local frames = display.newFrames("robot_knockout_%02d.png",0,5)
local animation = display.newAnimation(frames,1/12)
display.setAnimationCache(ROBOT_KNOCKOUT,animation)
self.KnockOutAction = CCSequence:createWithTwoActions(CCAnimate:create(animation),CCBlink:create(2,10))
end
function Robot:getWalkSpeed()
return WalkSpeed
end
function Robot:getCenterToSides()
return CenterToSide
end
function Robot:getCenterToBottom()
return CenterToBottom
end
function Robot:getDesiredPosition()
return self.DesiredPosition
end
function Robot:setDesiredPosition(param)
self.DesiredPosition = param
end
function Robot:getActionState()
return self.ActionState
end
function Robot:setActionState(param)
self.ActionState = param
end
function Robot:getVelocity()
return self.Velocity
end
function Robot:setVelocity(param)
self.Velocity = param
end
function Robot:getWalkAction()
return self.WalkAction
end
function Robot:getAttackAction()
return self.AttackAction
end
function Robot:getIdleAction()
return self.IdleAction
end
function Robot:getHurtAction()
return self.HurtAction
end
function Robot:getKnockOutAction()
return self.KnockOutAction
end
function Robot:setHitBox(box)
self.HitBox = box
end
function Robot:getHitBox()
return self.HitBox
end
function Robot:setAttackBox(box)
self.AttackBox = box
end
function Robot:getAttackBox()
return self.AttackBox
end
function Robot:getHurtPoint()
return self.HurtPoint
end
function Robot:getDamage()
return Damage
end
function Robot:setHurtPoint(param)
self.HurtPoint = param
end
function Robot:getNextDecisionTime()
return self.NextDecisionTime
end
function Robot:setNextDecisionTime(param)
self.NextDecisionTime = param
end
function Robot:idle()
Prototype.idle(self)
end
function Robot:attack()
Prototype.attack(self)
end
function Robot:playAttackSound()
audio.playSound(GAME_SFX.HIT1)
end
function Robot:playDeathSound()
audio.playSound(GAME_SFX.BOT_DEATH)
end
function Robot:hurtWithDamage(damage)
Prototype.hurtWithDamage(self,damage)
end
function Robot:walkWithDirection(direction)
Prototype.walkWithDirection(self,direction)
end
return Robot
打开 Hero.lua 添加构造函数中的 包围盒创建, 受伤,死亡动作,以及他们的 getter and setter
Hero.lua 的构造函数修改如下
2
3
4
5
6
7
8
9
10
11
12
WalkSpeed = 80
self.HurtPoint = 100
Damage = 20
CenterToSide = 29
CenterToBottom = 39
self.Name = name
self.HitBox = Prototype.createBoundingBoxWithOrigin(self,ccp(-self:getCenterToSides(), -self:getCenterToBottom()),
CCSizeMake(self:getCenterToSides() * 2, self:getCenterToBottom() * 2))
self.AttackBox = Prototype.createBoundingBoxWithOrigin(self,ccp(self:getCenterToSides(), -10), CCSizeMake(20, 20));
end
添加死亡和受伤函数 以及其他 getter setter
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
local frames = display.newFrames("hero_hurt_%02d.png",0,3)
local animation = display.newAnimation(frames,1/12)
display.setAnimationCache(HERO_HURT,animation)
local idelFunc = CCCallFunc:create(function() self:idle() end)
self.HurtAction = CCSequence:createWithTwoActions(CCAnimate:create(animation),idelFunc)
end
function Hero:createKnockOutAction()
local frames = display.newFrames("hero_knockout_%02d.png",0,5)
local animation = display.newAnimation(frames,1/12)
display.setAnimationCache(HERO_KNOCKOUT,animation)
self.KnockOutAction = CCSequence:createWithTwoActions(CCAnimate:create(animation),CCBlink:create(2,10))
end
function Hero:getHurtAction()
return self.HurtAction
end
function Hero:getKnockOutAction()
return self.KnockOutAction
end
function Hero:setHitBox(box)
self.HitBox = box
end
function Hero:getHitBox()
return self.HitBox
end
function Hero:setAttackBox(box)
self.AttackBox = box
end
function Hero:getAttackBox()
return self.AttackBox
end
function Hero:hurtWithDamage(damage)
Prototype.hurtWithDamage(self,damage)
end
现在,是时候在场景上添加Robot了
修改 GameLayer.lua 的构造函数如下
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
self.Actors = display.newBatchNode(CONFIG_ROLE_SHEET_IMAGE)
self:addChild(self.Actors,1)
self.ActorList = {}
self.RobotList = {}
self.CurrentIndex = 0
self:initTileMap()
self:initHero();
self:initRobots();
self.touchLayer = display.newLayer()
self:addChild(self.touchLayer,2)
self:setNodeEventEnabled(true)
local updateFunc = function(dt) self:onUpdate(dt) end
self:scheduleUpdate(updateFunc)
end
这里创建一个 RobotList 来过滤掉主角,单独保存机器人
在构造函数前 添加一个 TimeVal 变量,用于之后计算机器人思考的间隔时间
local TimeVal = 0
添加一个 local RobotCount = 50 用来决定机器人的数量
实现 initHero();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for RobotIndex = 1, RobotCount do
local RobotCell = Robot.new("Robot"..RobotIndex)
self:addActors(RobotCell)
self.RobotList[#self.RobotList + 1] = RobotCell
local minX = SCREEN_SIZE.width + RobotCell:getCenterToSides()
local maxX = TileMap:getMapSize().width * TileMap:getTileSize().width - RobotCell:getCenterToSides()
local minY = RobotCell:getCenterToBottom()
local maxY = 3 * TileMap:getTileSize().height + RobotCell:getCenterToBottom()
RobotCell.getClass().setPosition(RobotCell,math.random(minX, maxX),math.random(minY, maxY))
RobotCell:setScaleX(-1)
RobotCell:setDesiredPosition(ccp(RobotCell:getPositionX(),RobotCell:getPositionY()))
RobotCell:idle()
end
end
添加 function GameLayer:updateRobots(dt) 函数,这个函数将负责创建一个简单的机器人AI
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
TimeVal = TimeVal + dt
local alive = 0
local distanceSQ
local randomChoice
local curTime = TimeVal * 1000
local count = #self.RobotList
local heroPosition = ccp(Hero:getPositionX(),Hero:getPositionY())
for i = 1, count do
local robot = self.RobotList[i]
local robotPosition = ccp(robot:getPositionX(),robot:getPositionY())
robot:update(dt)
if robot:getActionState() ~= ACTION_STATE_KNOCKOUT then
alive = alive + 1
if curTime > robot:getNextDecisionTime() then
distanceSQ = ccpDistanceSQ(robotPosition,heroPosition)
if distanceSQ <= 250 then
robot:setNextDecisionTime(curTime + math.random() * 1000)
randomChoice = math.random(0,1)
if randomChoice <= 0.2 then
if heroPosition.x > robotPosition.x then
robot:setScaleX(1)
else
robot:setScaleX(-1)
end
robot:setNextDecisionTime(curTime + math.random() * 2000)
robot:attack()
if robot:getActionState() == ACTION_STATE_ATTACK then
if math.abs(heroPosition.y - robotPosition.y) < 10 then
if Hero:getHitBox().actual:intersectsRect(robot:getAttackBox().actual) then
Hero:hurtWithDamage(robot:getDamage())
end
end
end
else
robot:idle()
end
elseif distanceSQ <= SCREEN_SIZE.width * SCREEN_SIZE.width then
robot:setNextDecisionTime(curTime + math.random() * 1000)
randomChoice = math.random(0,2)
if randomChoice == 0 then
local moveDirection = ccpNormalize(ccpSub(heroPosition,robotPosition))
robot:walkWithDirection(moveDirection)
else
robot:idle()
end
end
end
end
end
end
TimeVal 会不断累加帧时间,然后通过消逝的时间长度来觉得机器人下一次的思考时间,并存进NextDecisionTime 中
逻辑如下,当机器人和玩家距离 小于250像素的时候, 记性一次随机数判断,有20%的概率判断机器人转身,并攻击玩家,其他情况下原地发呆
当机器人y坐标和英雄y坐标小于10,并且包围盒碰撞,则对英雄造成伤害。
其余的情况,机器人会朝着英雄靠近
之后我们将这个函数添加进update函数中,如下
2
3
4
5
6
7
Hero:update(dt)
self:updatePositions()
self:setViewPointCenter(Hero:getPosition());
self:renderActors()
self:updateRobots(dt)
end
最后修改 updatePositions() 函数,遍历我们的机器人,让机器人更新他们的坐标,以及避免他们走出地图
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
local position = Hero:getDesiredPosition()
local posX
local posY
if position.x ~= Hero:getPositionX() or position.y ~= Hero:getPositionY() then
posX = math.min(TileMap:getMapSize().width * TileMap:getTileSize().width - Hero:getCenterToSides(),
math.max(Hero:getCenterToSides(), position.x))
posY = math.min(3 * TileMap:getTileSize().height + Hero:getCenterToBottom(),
math.max(Hero:getCenterToBottom(), position.y))
Hero.getClass().setPosition(Hero,posX,posY)
end
local count = #self.RobotList
for i = 1, count do
local robot = self.RobotList[i]
position = robot:getDesiredPosition()
if position.x ~= robot:getPositionX() or position.y ~= robot:getPositionY() then
posX = math.min(TileMap:getMapSize().width * TileMap:getTileSize().width - robot:getCenterToSides(),
math.max(robot:getCenterToSides(), position.x))
posY = math.min(3 * TileMap:getTileSize().height + robot:getCenterToBottom(),
math.max(robot:getCenterToBottom(), position.y))
robot.getClass().setPosition(robot,posX,posY)
end
end
end
接下来,运行游戏,你就能看到一堆机器人开始冲向你了 :D
最后给游戏添加音效吧~
打开 config.lua 添加音效的 名称 和 对应的地址
2
3
4
5
6
7
8
GAME_SFX = {
HIT0 = "pd_hit0.wav",
HIT1 = "pd_hit1.wav",
HERO_DEATH = "pd_herodeath.wav",
BOT_DEATH = "pd_botdeath.wav",
BGM = "LevelWinSound.mp3",
}
之后打开 game.lua 让我们在进入游戏前,先预载这些音乐
在 startup() 函数中 game.enterMainScene() 的前一行加上
2
3
4
for k, v in pairs(GAME_SFX) do
audio.preloadSound(v)
end
然后我们在进入场景后,播放背景音乐
打开 GameScene.lua 在构造函数的最后一行添加 第一个参数是音效名称,第二个参数是循环播放
如果找不到GAME_SFX.BGM, 我们需要先 require(“config”)
接着在 Hero 和 Robot 中添加 下面的函数, 如果没有 require(“config”), 也需要加上
Hero.lua
2
3
4
5
6
7
audio.playSound(GAME_SFX.HIT0)
end
function Hero:playDeathSound()
audio.playSound(GAME_SFX.HERO_DEATH)
end
Robot.lua
2
3
4
5
6
7
audio.playSound(GAME_SFX.HIT1)
end
function Robot:playDeathSound()
audio.playSound(GAME_SFX.BOT_DEATH)
end
最后修改 ActionSprite.lua 在攻击和死亡的时候调用之前的函数
在 knockout() 函数的最后一行加上
self:playDeathSound()
在 attack() 函数的 if then — end 结构内的最后一行加上
self:playAttackSound()
之后~运行你的游戏看看吧~
本游戏的代码已开源,包含游戏资源
git地址
https://github.com/dreamfairy/PrompaLua
Tags: Cocos2d-x, lua
Trackback from your site.
- 使用 Cosos2dX-Lua 制作一个横版过关游戏 (3)
- 使用 Cosos2dX-Lua 制作一个横版过关游戏 (2)
- 使用 Cosos2dX-Lua 制作一个横版过关游戏 (1)
- 使用 Cosos2dX-Lua 制作一个横版过关游戏 (2)
- 如何制作一个横版格斗过关游戏 Cocos2d-x
- 如何制作一个横版格斗过关游戏 Cocos2d-x 2.0.4
- 如何制作一个横版格斗过关游戏(2) Cocos2d-x 2.0.4
- 如何制作一个横版格斗过关游戏(一) Cocos2d-x 2.0.4
- 如何制作一个横版格斗过关游戏(二) Cocos2d-x 2.0.4
- 如何制作一个横版格斗过关游戏 Cocos2d-x 2.0.4
- 如何制作一个横版格斗过关游戏 Cocos2d-x 2.0.4
- Cocos2d-x 2.0.4 如何制作一个横版格斗过关游戏
- Cocos2d-x 2.0.4 如何制作一个横版格斗过关游戏(2)
- 如何制作一个横版格斗过关游戏 Cocos2d-x 2.0.4
- Cocos2d-x 2.0.4 如何制作一个横版格斗过关游戏
- Cocos2d-x 2.0.4 如何制作一个横版格斗过关游戏(2)
- 如何制作一个横版格斗过关游戏(2) Cocos2d-x 2.0.4
- 如何制作一个横版格斗过关游戏 Cocos2d-x 2.0.4
- ie-css3.htc没效果
- EL表达式不起作用的问题
- HIVE 分区表添加字段后的奇怪现象
- 使用 Cosos2dX-Lua 制作一个横版过关游戏 (2)
- Linux socket 网络编程 常用头文件
- 使用 Cosos2dX-Lua 制作一个横版过关游戏 (3)
- QT 窗体固定大小
- UVa:12086 Potentiometers
- 常见Java面试题 – 第一部分:非可变性(Immutability)和对象引用(Object reference)
- juery插件文件上传
- 重新认识Android Activity的生命周期
- 给傻瓜用的SP2010开发--第一部分--理解SP开发平台--第一章节--理解SP促销讨论(2)--追踪SP源头
- 那哪些控件或标签具有onload事件呢
- js实现滚动时层智能浮动定位