游戏说明
游戏开始,下方随机生成不同品质小人,在场景中来回走动,不同品质的小人,移动速度不一样,被抓到后获得的积分也不一样。按住移动按钮,上方机械手臂会左右来回移动,当松开按钮,机械手臂会掉下来,当手臂下落碰到场景中小人时,检测当前手臂抓取范围内的小人,在抓取范围内的小人会被抓走。然后计算获得分数,如果机械手臂抓空,则碰到地面后手臂也会自动回弹,每轮游戏有4次抓取机会。
功能实现
移动的小人
创建移动小人AmusementParkDollMachineGameRole,跟据不同品质小人,移动速度不同实现小人在可控范围内随机位置移动。在移动过程中通过修改sortingOrder值来调整小人在不同位置的层级显示。小人在移动是播放walk动作,被抓住时拖放drag动作,同时将小人从场景节点移动到机械手臂抓取小人的节点内,当手臂上升时,小人跟着手臂一起向上移动,抓取结束移除被抓取的小人。
1
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
---待抓取的游戏小人
---@class AmusementParkDollMachineGameRole
local m = {
View = nil,
---@type AmusementParkDollMachineRoleData
RoleData = nil,
---@type Spine.Unity.SkeletonGraphic
Animation = nil,
---@type number
EntityId = 0,
---@type Vector3 下一个移动位置
NextPos = Vector3.New(0,0,0),
---@type boolean 是否在移动中
IsMoving = false,
---@type number 下一次开始移动的时间
NextMovieTimer = 0,
---@type boolean 小人是否已成功创建
IsCreateRole = false,
---@type boolean 小人是否被抓住
IsCatch = false,
---@type UnityEngine.Collider2D 小人Collider
RoleCollider = nil,
RoleObj = nil,
}
function m.New(view)
local obj = Clone(m)
obj:Init(view)
return obj
end
function m:Init(view)
self.View = view
LuaCodeInterface.BindOutlet(self.View, self)
self.OnShowSpineSuccessDelegate = function(sender, args) self:OnShowSpineSuccess(sender, args) end
self.RoleCollider = self.View:GetComponent(typeof(UnityEngine.Collider2D))
self:AddListener()
end
function m:AddListener()
GameEntry.LuaEvent:Subscribe(UnityGameFramework.Runtime.ShowEntitySuccessEventArgs.EventId, self.OnShowSpineSuccessDelegate)
end
function m:RemoveListener()
GameEntry.LuaEvent:Unsubscribe(UnityGameFramework.Runtime.ShowEntitySuccessEventArgs.EventId, self.OnShowSpineSuccessDelegate)
end
---界面轮询。
---@param elapseSeconds number
---@param realElapseSeconds number
---@return void
function m:OnUpdate(elapseSeconds, realElapseSeconds)
if not self.IsCreateRole then
return
end
if self.RoleData == nil then
return
end
if self.IsCatch then
return
end
if not self.IsMoving then
if PlayerModule.GetServerTime() > self.NextMovieTimer then
self:StartMove()
end
return
end
local direction = self.NextPos - self.View.transform.localPosition
if direction.magnitude > self.RoleData.roleRareSpeed then
self.View.transform:Translate(direction.normalized * self.RoleData.roleRareSpeed * UnityEngine.Time.deltaTime)
self:ChangeRoleOrderZ()
else
self:StopMove()
end
end
function m:ChangeOrder()
if self.RoleObj then
local canvas = self.View:GetComponentInParent(typeof(UnityEngine.Canvas))
setGameObjectLayers(self.RoleObj.transform, "UI")
local renderers = self.RoleObj:GetComponentsInChildren(typeof(UnityEngine.Renderer))
for i = 0, renderers.Length - 1 do
renderers[i].sortingLayerID = LuaCodeInterface.SortingLayerNameToID("Default")
renderers[i].sortingOrder = canvas.sortingOrder + 1
end
end
end
function m:OnShowSpineSuccess(sender, args)
if self.EntityId == args.Entity.Id then
self.RoleObj = args.Entity.gameObject
self.Animation = args.Entity.gameObject:GetComponentInChildren(typeof(Spine.Unity.SkeletonAnimation))
self.Animation.state:SetAnimation(0,"idle", true)
self:StartMove()
self.IsCreateRole = true
self:ChangeOrder()
end
end
---获取一个随机位置 坐标Z控制小人显示层级
function m:GetRandomPos()
local rx = math.random(-self.View.transform.parent.rect.w/2,self.View.transform.parent.rect.w/2)
local ry = math.random(-self.View.transform.parent.rect.h/2,self.View.transform.parent.rect.h/2)
return Vector3.New(rx,ry,0)
end
---开始移动
function m:StartMove()
self.IsMoving = true
self.NextPos = self:GetRandomPos()
if self.Animation then
self.Animation.state:SetEmptyAnimation(0, 0)
self.Animation.state:SetAnimation(0, "walk", true)
end
self:RefreshWalkFaceTo()
end
---移动到目标位置停止移动
function m:StopMove()
self.IsMoving = false
self.View.transform.localPosition = self.NextPos
self.NextMovieTimer = math.random(0,4) + PlayerModule.GetServerTime()
if self.Animation then
self.Animation.state:SetAnimation(0,"idle", true)
end
end
---播放抓住状态
function m:CatchState()
self.IsMoving = false
self.IsCatch = true
if self.Animation then
self.Animation.AnimationName = nil
self.Animation.state:SetEmptyAnimation(0, 0)
self.Animation.state:SetAnimation(0,"drag", true)
end
end
---刷新移动朝向
function m:RefreshWalkFaceTo()
local checkRet = self.NextPos.x - self.View.transform.localPosition.x
local localScale = self.View.transform.localScale
if checkRet > 0 then
localScale.x = -math.abs(localScale.x)
else
localScale.x = math.abs(localScale.x)
end
self.View.transform.localScale = localScale
end
---@param roleData AmusementParkDollMachineRoleData
function m:SetData(roleData)
self.RoleData = roleData
self.IsCreateRole = false
self.IsCatch = false
for i = 1, 4 do
self["Quality"..i]:SetActive(self.RoleData.roleRare == i)
end
self.View.name = self.RoleData.cid
local pos = self:GetRandomPos()
self.View.transform.localPosition = pos
self:ChangeRoleOrderZ()
self.NextPos = self:GetRandomPos()
self:ShowSpine()
end
---改变小人移动Z值(移动是层级关系)
function m:ChangeRoleOrderZ()
local pos = self.View.transform.localPosition
---设置小人移动的Z值
self.OrderZ.transform.localPosition = Vector3.New(0,0,(pos.y + 150)/100)
end
function m:ShowSpine()
self:HideEntity()
self.EntityId = GameEntry.Entity:GenerateEntityID()
GameEntry.Entity:ShowUIElement(self.EntityId, self.RoleData:GetCfgData().PlayerSpine, 0, self.SpineRoot.transform, Vector3.zero)
end
function m:HideEntity()
if self.EntityId ~= 0 then
GameEntry.Entity:HideEntity(self.EntityId)
self.EntityId = 0
end
end
function m:Dispose()
AssetUtil.UnloadAsset(self)
self:RemoveListener()
self:HideEntity()
LuaCodeInterface.ClearOutlet(self.View, self)
UnityEngine.Object.Destroy(self.View)
self.View = nil
end
return m
机械手臂
创建AmusementParkDollMachineArm实现机械手臂功能。机械手臂有两个不同的Collider2D,向下抓取移动时的碰撞检测Collider2D和抓取小人范围Collider2D。碰撞检测Collider2D小于抓取范围的Collider2D。通过transform.Translate()方法,实现手臂左右移动和抓取操作。
1
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
---界面轮询。
---@param elapseSeconds number
---@param realElapseSeconds number
---@return void
function m:OnUpdate(elapseSeconds, realElapseSeconds)
if self.IsCatching then
self.CollisionNode.transform:Translate(Vector3.New(0,self.CatchSpeed * self.CatchDir * UnityEngine.Time.deltaTime, 0))
local top = self:CheckCatch()
---碰到小人或者地面手臂上升
if top then
self.CatchDir = 1
if not self.CatchEnd then
self.CatchEnd = true
self.CatchRoles = self:CheckCatchRoleResult()
end
end
if self.CollisionNode.transform.localPosition.y >= 0 then
self.CollisionNode.transform.localPosition = Vector3.New(0,0,0)
self.IsCatching = false
self.CatchEnd = false
self.RootUI:CleanRoles(self.CatchRoles)
end
else
if self.IsMove then
if self.View.transform.localPosition.x >= self.MaxPosX then
self.Dir = -1
elseif self.View.transform.localPosition.x <= self.MinPosX then
self.Dir = 1
end
self.View.transform:Translate(Vector3.New(self.MoveSpeed * self.Dir * UnityEngine.Time.deltaTime, 0, 0))
end
end
end
抓取小人
机械手臂下降过程中,通过CollisionNode节点上的Collider2D检测碰撞结果,如果成功碰撞到小人,则启用GrabItNode节点下的Collider2D检测抓取范围内的小人。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
---检测抓子抓到的小人
---@return AmusementParkDollMachineRoleData []
function m:CheckCatchRoleResult()
local roles = {}
for i, v in pairs(self.RootUI.GameRoles) do
local top = self.HandGrabCollider2D.bounds:Intersects(v.RoleCollider.bounds)
if top then
v.View.transform:SetParent(self.RolesNode.transform,false)
v.View.transform.localPosition = Vector3.New(0,0,0)
table.insert(roles,v.RoleData)
v:CatchState()
end
end
return roles
end
---检测机器手臂下降过程中碰撞
---@param collider2D UnityEngine.Collider2D 目标对象collider2D
---@return boolean
function m:CheckArmBoundsIntersects(collider2D)
return self.ArmCollider2D.bounds:Intersects(collider2D.bounds)
end
实例:
1
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
168
169
170
---@class AmusementParkDollMachineArm
local m = {
View = nil,
---@type AmusementParkGamePrizeClawUI
RootUI = nil,
---@type boolean 是否在移动中 按下按钮开始移动,抬起停止移动
IsMove = false,
---@type number 横向移动最小值
MinPosX = -425,
---@type number 横向移动最大值
MaxPosX = 425,
---@type number 横向移动速度
MoveSpeed = 6,
---@type number 移动方向 向右为正
Dir = 1,
---@type boolean 正在向下抓取中
IsCatching = false,
---@type number 抓取速度
CatchSpeed = 9,
---@type number 抓取移动方向 向下
CatchDir = -1,
---@type AmusementParkDollMachineRoleData 成功抓到的小人
CatchRoles = {},
---@type UnityEngine.Collider2D 机器臂下降Collider
ArmCollider2D = nil,
---@type UnityEngine.Collider2D 手抓Collider
HandGrabCollider2D = nil,
---@type boolean
CatchEnd = false,
}
function m.New(view,rootUI)
local obj = Clone(m)
obj:Init(view,rootUI)
return obj
end
function m:Init(view,rootUI)
self.View = view
self.RootUI = rootUI
LuaCodeInterface.BindOutlet(self.View, self)
self.ArmCollider2D = self.CollisionNode:GetComponent(typeof(UnityEngine.Collider2D))
self.HandGrabCollider2D = self.GrabItNode:GetComponent(typeof(UnityEngine.Collider2D))
self:AddListener()
end
---界面轮询。
---@param elapseSeconds number
---@param realElapseSeconds number
---@return void
function m:OnUpdate(elapseSeconds, realElapseSeconds)
if self.IsCatching then
self.CollisionNode.transform:Translate(Vector3.New(0,self.CatchSpeed * self.CatchDir * UnityEngine.Time.deltaTime, 0))
local top = self:CheckCatch()
---碰到小人或者地面手臂上升
if top then
self.CatchDir = 1
if not self.CatchEnd then
self.CatchEnd = true
self.CatchRoles = self:CheckCatchRoleResult()
end
end
if self.CollisionNode.transform.localPosition.y >= 0 then
self.CollisionNode.transform.localPosition = Vector3.New(0,0,0)
self.IsCatching = false
self.CatchEnd = false
self.RootUI:CleanRoles(self.CatchRoles)
end
else
if self.IsMove then
if self.View.transform.localPosition.x >= self.MaxPosX then
self.Dir = -1
elseif self.View.transform.localPosition.x <= self.MinPosX then
self.Dir = 1
end
self.View.transform:Translate(Vector3.New(self.MoveSpeed * self.Dir * UnityEngine.Time.deltaTime, 0, 0))
end
end
end
function m:AddListener()
end
function m:RemoveListener()
end
---重游戏
function m:ResetGame()
self.IsMove = false
self.Dir = 1
self.View.transform.localPosition = Vector3.New(self.MinPosX,self.View.transform.localPosition.y,self.View.transform.localPosition.z)
self.IsCatching = false
self.CatchEnd = false
self.CatchDir = -1
self.CollisionNode.transform.localPosition = Vector3.New(0,0,0)
self.CatchRoles = {}
end
---开始移动位置
function m:StartMoveArm()
if self.IsCatching then
return
end
self.IsMove = true
end
---开始抓取
function m:StopMoveAndStartCatch()
self.IsMove = false
self.IsCatching = true
self.CatchDir = -1
self.CollisionNode.transform.localPosition = Vector3.New(0,0,0)
end
---检测手臂碰撞结果(如果有成功碰到小人或者地面返回true)
---@return boolean
function m:CheckCatch()
local top = false
---先检测小人
for i, v in pairs(self.RootUI.GameRoles) do
top = self:CheckArmBoundsIntersects(v.RoleCollider)
if top then
return top
end
end
---再检测地面
top = self:CheckArmBoundsIntersects(self.RootUI.GroundCollider)
return top
end
---检测抓子抓到的小人
---@return AmusementParkDollMachineRoleData []
function m:CheckCatchRoleResult()
local roles = {}
for i, v in pairs(self.RootUI.GameRoles) do
local top = self.HandGrabCollider2D.bounds:Intersects(v.RoleCollider.bounds)
if top then
v.View.transform:SetParent(self.RolesNode.transform,false)
v.View.transform.localPosition = Vector3.New(0,0,0)
table.insert(roles,v.RoleData)
v:CatchState()
end
end
return roles
end
---检测机器手臂下降过程中碰撞
---@param collider2D UnityEngine.Collider2D 目标对象collider2D
---@return boolean
function m:CheckArmBoundsIntersects(collider2D)
return self.ArmCollider2D.bounds:Intersects(collider2D.bounds)
end
function m:Dispose()
AssetUtil.UnloadAsset(self)
self:RemoveListener()
self.IsMove = false
self.CatchRoles = {}
self.IsCatching = false
LuaCodeInterface.ClearOutlet(self.View, self)
UnityEngine.Object.Destroy(self.View)
self.View = nil
end
return m