-
Notifications
You must be signed in to change notification settings - Fork 42
/
collision.go
executable file
·158 lines (118 loc) · 4.73 KB
/
collision.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
package resolv
// Collision contains the results of an Object.Check() call, and represents a collision between an Object and cells that contain other Objects.
// The Objects array indicate the Objects collided with.
type Collision struct {
checkingObject *Object // The checking object
dx, dy float64 // The delta the checking object was moving on that caused this collision
Objects []*Object // Slice of objects that were collided with; sorted according to distance to calling Object.
Cells []*Cell // Slice of cells that were collided with; sorted according to distance to calling Object.
}
func newCollision() *Collision {
return &Collision{
Objects: []*Object{},
}
}
// HasTags returns whether any objects within the Collision have all of the specified tags. This slice does not contain the Object that called Check().
func (cc *Collision) HasTags(tags ...string) bool {
for _, o := range cc.Objects {
if o == cc.checkingObject {
continue
}
if o.HasTags(tags...) {
return true
}
}
return false
}
// ObjectsByTags returns a slice of Objects from the cells reported by a Collision object by searching for Objects with a specific set of tags.
// This slice does not contain the Object that called Check().
func (cc *Collision) ObjectsByTags(tags ...string) []*Object {
objects := []*Object{}
for _, o := range cc.Objects {
if o == cc.checkingObject {
continue
}
if o.HasTags(tags...) {
objects = append(objects, o)
}
}
return objects
}
// ContactWithObject returns the delta to move to have the checking object come into contact with the specified Object.
func (cc *Collision) ContactWithObject(object *Object) Vector {
delta := Vector{0, 0}
if cc.dx < 0 {
delta.X = object.Position.X + object.Size.X - cc.checkingObject.Position.X
} else if cc.dx > 0 {
delta.X = object.Position.X - cc.checkingObject.Size.X - cc.checkingObject.Position.X
}
if cc.dy < 0 {
delta.Y = object.Position.Y + object.Size.Y - cc.checkingObject.Position.Y
} else if cc.dy > 0 {
delta.Y = object.Position.Y - cc.checkingObject.Size.Y - cc.checkingObject.Position.Y
}
return delta
}
// ContactWithCell returns the delta to move to have the checking object come into contact with the specified Cell.
func (cc *Collision) ContactWithCell(cell *Cell) Vector {
delta := Vector{0, 0}
cx := float64(cell.X * cc.checkingObject.Space.CellWidth)
cy := float64(cell.Y * cc.checkingObject.Space.CellHeight)
if cc.dx < 0 {
delta.X = cx + float64(cc.checkingObject.Space.CellWidth) - cc.checkingObject.Position.X
} else if cc.dx > 0 {
delta.X = cx - cc.checkingObject.Size.X - cc.checkingObject.Position.X
}
if cc.dy < 0 {
delta.Y = cy + float64(cc.checkingObject.Space.CellHeight) - cc.checkingObject.Position.Y
} else if cc.dy > 0 {
delta.Y = cy - cc.checkingObject.Size.Y - cc.checkingObject.Position.Y
}
return delta
}
// SlideAgainstCell returns how much distance the calling Object can slide to avoid a collision with the targetObject, and
// a boolean indicating if such a slide was possible.
// This only works on vertical and horizontal axes (x and y directly), primarily for platformers / top-down games.
// avoidTags is a sequence of tags (as strings) to indicate when sliding is valid (i.e. if a Cell contains an
// Object that has the tag given in the avoidTags slice, then sliding CANNOT happen).
func (cc *Collision) SlideAgainstCell(cell *Cell, avoidTags ...string) (Vector, bool) {
sp := cc.checkingObject.Space
collidingCell := cc.Cells[0]
ccX, ccY := sp.SpaceToWorld(collidingCell.X, collidingCell.Y)
hX := float64(sp.CellWidth) / 2.0
hY := float64(sp.CellHeight) / 2.0
ccX += hX
ccY += hY
center := cc.checkingObject.Center()
diffX := center.X - ccX
diffY := center.Y - ccY
left := sp.Cell(collidingCell.X-1, collidingCell.Y)
right := sp.Cell(collidingCell.X+1, collidingCell.Y)
up := sp.Cell(collidingCell.X, collidingCell.Y-1)
down := sp.Cell(collidingCell.X, collidingCell.Y+1)
slide := Vector{0, 0}
// Moving vertically
if cc.dy != 0 {
if diffX > 0 && (right == nil || !right.ContainsTags(avoidTags...)) {
// Slide right
slide.X = ccX + hX - cc.checkingObject.Position.X
} else if diffX < 0 && (left == nil || !left.ContainsTags(avoidTags...)) {
// Slide left
slide.X = ccX - hX - (cc.checkingObject.Position.X + cc.checkingObject.Size.X)
} else {
return Vector{}, false
}
}
if cc.dx != 0 {
if diffY > 0 && (down == nil || !down.ContainsTags(avoidTags...)) {
// Slide down
slide.Y = ccY + hY - cc.checkingObject.Position.Y
} else if diffY < 0 && (up == nil || !up.ContainsTags(avoidTags...)) {
// Slide up
slide.Y = ccY - hY - (cc.checkingObject.Position.Y + cc.checkingObject.Size.Y)
} else {
return Vector{}, false
}
}
return slide, true
}