Last active
March 15, 2021 04:47
-
-
Save ftsf/030f429f67807c2ad1395bb936fce934 to your computer and use it in GitHub Desktop.
nico separating axis theorem collision check
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import nico | |
import nico/vec | |
type Object = ref object | |
color: int | |
pos: Vec2f | |
points: seq[Vec2f] | |
rotation: float32 | |
var objs: seq[Object] | |
var overlapping = false | |
var overlapAxis: Vec2f | |
var overlapAmount: float32 | |
var separate = false | |
iterator edges(self: Object): (Vec2f,Vec2f) = | |
let pos = self.pos | |
for i in 1..<self.points.len: | |
let a = self.points[i-1].rotate(self.rotation) | |
let b = self.points[i].rotate(self.rotation) | |
yield (pos + a, pos + b) | |
block: | |
let a = self.points[self.points.high].rotate(self.rotation) | |
let b = self.points[0].rotate(self.rotation) | |
yield (pos + a, pos + b) | |
proc getAxes(a,b: Object): seq[Vec2f] = | |
for edge in a.edges: | |
let axis = (edge[1] - edge[0]).perpendicular().normalized | |
if axis notin result: | |
result.add(axis) | |
for edge in b.edges: | |
let axis = (edge[1] - edge[0]).perpendicular().normalized | |
if axis notin result: | |
result.add(axis) | |
proc sat(a,b: Object): (bool,Vec2f,float32) = | |
## return true if objects are overlapping, if overlapping returns the axis of min overlap | |
var axisOfMinOverlap: Vec2f | |
var minOverlap: float32 = Inf | |
for axis in getAxes(a,b): | |
var amin = Inf | |
var bmin = Inf | |
var amax = -Inf | |
var bmax = -Inf | |
# project each edge against the current axis | |
for edge in a.edges: | |
for p in [edge[0],edge[1]]: | |
var v = dot(p,axis) | |
if v < amin: | |
amin = v | |
if v > amax: | |
amax = v | |
for edge in b.edges: | |
for p in [edge[0],edge[1]]: | |
var v = dot(p,axis) | |
if v < bmin: | |
bmin = v | |
if v > bmax: | |
bmax = v | |
if bmin > amax or amin > bmax: | |
# found axis of separation, we can exit early | |
result[0] = false | |
return | |
var overlap = 0f | |
if amax > bmin: | |
overlap = abs(bmin - amax) | |
elif bmax > amin: | |
overlap = abs(amin - bmax) | |
if abs(overlap) < abs(minOverlap): | |
minOverlap = overlap | |
axisOfMinOverlap = axis | |
return (true, axisOfMinOverlap, minOverlap) | |
proc draw(self: Object) = | |
setColor(self.color) | |
for edge in self.edges: | |
line(edge[0], edge[1]) | |
proc gameInit() = | |
# we want a fixed sized screen with perfect square pixels | |
fixedSize(true) | |
integerScale(true) | |
# create the window | |
nico.createWindow("nico",128,128,4) | |
objs = @[] | |
objs.add(Object(color: 6, pos: vec2f(64,64), points: @[vec2f(-16f, -16f), vec2f(16f, -16f), vec2f(16f, 16f), vec2f(-16f, 16f)])) | |
objs.add(Object(color: 5, pos: vec2f(82,64), points: @[vec2f(-32f, -16f), vec2f(16f, -16f), vec2f(16f, 16f), vec2f(-16f, 16f)])) | |
proc gameUpdate(dt: float32) = | |
objs[0].rotation += 0.5f * dt | |
let (mx,my) = mouse() | |
if mousebtn(0): | |
objs[1].pos = vec2f(mx,my) | |
if btnp(pcA): | |
separate = not separate | |
let hit = sat(objs[0], objs[1]) | |
overlapping = hit[0] | |
overlapAxis = hit[1] | |
overlapAmount = hit[2] | |
if overlapping and separate: | |
# push them apart | |
objs[0].pos += overlapAxis * (overlapAmount * -0.5f) | |
objs[1].pos += overlapAxis * (overlapAmount * 0.5f) | |
proc gameDraw() = | |
cls() | |
for obj in objs: | |
obj.draw() | |
if overlapping: | |
setColor(8) | |
print("overlapping", 4, 4) | |
if overlapping: | |
setColor(8) | |
line(objs[0].pos, objs[0].pos - overlapAxis * overlapAmount) | |
# initialization | |
nico.init("nico", "sat") | |
# start, say which functions to use for init, update and draw | |
nico.run(gameInit, gameUpdate, gameDraw) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment