-
Notifications
You must be signed in to change notification settings - Fork 0
/
game_object.go
240 lines (183 loc) · 6.02 KB
/
game_object.go
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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
package engine
// GameObject represented a sprite and its properties
type GameObject struct {
CurrentState string
States GameObjectStates
Position Vector
Mass float64
Velocity Vector
Direction int
IsFlipped bool
IsControllable bool
IsFloor bool
IsInteractive bool
IsHidden bool
Level *Level
DynamicData DynamicData
FloorY float64
EventHandler EventHandler
CollisionHandler CollisionHandler
}
// IsResting determined whether the game object is currently atop another game
// object
func (gameObject *GameObject) IsResting() bool {
// Special case for floating objects
if gameObject.Mass == 0 {
return false
}
return int(gameObject.Position.Y) == int(gameObject.FloorY)
}
// CurrentSprite gets the current sprite for the object's state
func (gameObject *GameObject) CurrentSprite() SpriteInterface {
spriteSeries := gameObject.States[gameObject.CurrentState]
sprite := gameObject.getCurrentSpriteFrame(spriteSeries)
return sprite
}
// getCurrentSpriteFrame gets the appropriate frame of a sprite series based on the
// game's frame ticker
func (gameObject *GameObject) getCurrentSpriteFrame(spriteSeries SpriteSeries) SpriteInterface {
// if we dont have a level
if gameObject.Level != nil {
game := gameObject.Level.Game
framePerSprite := (game.TargetFrameRate / spriteSeries.CyclesPerSecond) / len(spriteSeries.Sprites)
spriteCounter := 0
i := 0
for j := 0; j < game.TargetFrameRate; j++ {
i++
if i == framePerSprite {
i = 0
spriteCounter++
}
if spriteCounter >= len(spriteSeries.Sprites) {
spriteCounter = 0
}
if j == game.CurrentFrame {
return spriteSeries.Sprites[spriteCounter]
}
}
}
return spriteSeries.Sprites[0]
}
// Width gets width of the game object
func (gameObject *GameObject) Width() int {
return gameObject.CurrentSprite().Width()
}
// Height gets height of the game object
func (gameObject *GameObject) Height() int {
return gameObject.CurrentSprite().Height()
}
// RecalculatePosition recalculates the latest X and Y position of the game
// object from its properties
func (gameObject *GameObject) RecalculatePosition(gravity float64) {
// Move left or right
if gameObject.Direction == DirRight {
// Go right
gameObject.Position.X += gameObject.Velocity.X
} else if gameObject.Direction == DirLeft {
// Go left
gameObject.Position.X -= gameObject.Velocity.X
}
// Jump up (and/or be pulled down by gravity) if the floor is further down
if gameObject.FloorY <= gameObject.Position.Y {
wasAboveFloor := gameObject.Position.Y > gameObject.FloorY
gameObject.Position.Y += gameObject.Velocity.Y
gameObject.Velocity.Y -= (gravity * gameObject.Mass)
// If actively falling down, emit the 'freefall' event
if gameObject.Position.Y > gameObject.FloorY && gameObject.Velocity.Y < 0 {
gameObject.EventHandler(EventFreeFall, gameObject)
}
// Ensure the floor object acts as a barrier
if gameObject.Position.Y <= gameObject.FloorY && gameObject.Mass != 0 {
gameObject.Position.Y = gameObject.FloorY
gameObject.Velocity.Y = 0
if wasAboveFloor == true {
gameObject.EventHandler(EventFloorCollision, gameObject)
}
}
}
// Don't fall too far off-screen
if gameObject.Mass != 0 {
minYPos := float64(0 - gameObject.Height())
if gameObject.Position.Y <= minYPos {
gameObject.Position.Y = minYPos
if gameObject.IsInteractive == true {
gameObject.EventHandler(EventDropOffLevel, gameObject)
}
}
}
}
// SetDynamicData sets a piece of dynamic game object data
func (gameObject *GameObject) SetDynamicData(key string, value interface{}) {
gameObject.DynamicData[key] = value
}
// GetDynamicData gets a piece of dynamic game object data, falling back to a
// define value if the data does not exist
func (gameObject *GameObject) GetDynamicData(key string, fallback interface{}) interface{} {
if value, ok := gameObject.DynamicData[key]; ok {
return value
}
return fallback
}
// HasDynamicData checks whether a piece of dynamic data game object data exists
func (gameObject *GameObject) HasDynamicData(key string) bool {
if _, ok := gameObject.DynamicData[key]; ok {
return true
}
return false
}
// ClearDynamicData clears a piece of specific dynamic game object data
func (gameObject *GameObject) ClearDynamicData(key string) {
delete(gameObject.DynamicData, key)
}
// GetCollisionEdge infers edge on which an intersecting object collided
// with the game object
func (gameObject *GameObject) GetCollisionEdge(collidingObject *GameObject) string {
// where is the game object's outer edge in relation to the colliding
// object?
isLeft := gameObject.Position.X < collidingObject.Position.X
isRight := (gameObject.Position.X + float64(gameObject.Width())) > (collidingObject.Position.X + float64(collidingObject.Width()))
isBottom := gameObject.Position.Y < collidingObject.Position.Y
isTop := (gameObject.Position.Y +float64(gameObject.Height())) > (collidingObject.Position.Y + float64(collidingObject.Height()))
// If both objects are at the rest a simple 'left' or 'right' can be assumed
// regardless of the height of either object
if (gameObject.Mass == 0 || gameObject.IsResting() == true) &&
(collidingObject.Mass == 0 || collidingObject.IsResting() == true) {
if isLeft == true {
return EdgeLeft
}
if isRight == true {
return EdgeRight
}
}
// If there's any vertical movement, combination edges will make more sense
if isLeft == true && isTop == true {
return EdgeTopLeft
}
if isRight == true && isTop == true {
return EdgeTopRight
}
if isLeft == true && isBottom == true {
return EdgeBottomLeft
}
if isRight == true && isBottom == true {
return EdgeBottomRight
}
// Failing the above, it may be the case that the game object is smaller
// than the colliding object, and therefore fits nicely within one of the
// regular edges
if isLeft {
return EdgeLeft
}
if isRight {
return EdgeRight
}
if isTop {
return EdgeTop
}
if isBottom {
return EdgeBottom
}
// Should never reach here unless the game object the wholly withing the
// colliding object
return EdgeNone
}