123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- # author: Ethosa
- ## It provides collision shapes.
- import
- ../thirdparty/opengl,
- ../core/vector2,
- ../core/rect2,
- ../core/anchor,
- ../core/input,
- ../core/enums,
- ../core/circle2,
- ../nodes/node,
- node2d
- when defined(debug):
- import math
- const PI2 = PI*2
- type
- CollisionShape2DType* {.size: sizeof(int8), pure.} = enum
- COLLISION_SHAPE_2D_RECTANGLE,
- COLLISION_SHAPE_2D_CIRCLE,
- COLLISION_SHAPE_2D_POLYGON
- CollisionShape2DObj* = object of Node2DObj
- disable*: bool
- x1*, y1*, radius*: float
- polygon*: seq[Vector2Ref]
- shape_type*: CollisionShape2DType
- CollisionShape2DPtr* = ptr CollisionShape2DObj
- proc CollisionShape2D*(name: string, variable: var CollisionShape2DObj): CollisionShape2DPtr =
- ## Creates a new CollisionShape2D pointer.
- ##
- ## Arguments:
- ## - `name` is a node name.
- ## - `variable` is a CollisionShape2DObj variable.
- runnableExamples:
- var
- node_obj: CollisionShape2DObj
- node = CollisionShape2D("CollisionShape2D", node_obj)
- nodepattern(CollisionShape2DObj)
- node2dpattern()
- variable.rect_size.x = 40
- variable.rect_size.y = 40
- variable.disable = false
- variable.x1 = 0
- variable.y1 = 0
- variable.radius = 20
- variable.polygon = @[]
- variable.kind = COLLISION_SHAPE_2D_NODE
- proc CollisionShape2D*(obj: var CollisionShape2DObj): CollisionShape2DPtr {.inline.} =
- ## Creates a new CollisionShape2D pointer with deffault node name "CollisionShape2D".
- ##
- ## Arguments:
- ## - `variable` is a CollisionShape2DObj variable.
- runnableExamples:
- var
- node_obj: CollisionShape2DObj
- node = CollisionShape2D(node_obj)
- CollisionShape2D("CollisionShape2D", obj)
- method setShapeTypeRect*(self: CollisionShape2DPtr) {.base.} =
- ## Changes shape type to `circle`.
- self.shape_type = COLLISION_SHAPE_2D_RECTANGLE
- method setShapeTypeCircle*(self: CollisionShape2DPtr, cx, cy, radius: float) {.base.} =
- ## Changes shape type to `rectangle`.
- ##
- ## Arguments:
- ## - `cx` is a center circle position at X axis.
- ## - `cy` is a center circle position at Y axis.
- ## - `radius` is a circle radius.
- self.shape_type = COLLISION_SHAPE_2D_CIRCLE
- self.x1 = cx
- self.y1 = cy
- self.radius = radius
- method setShapeTypePolygon*(self: CollisionShape2DPtr, positions: varargs[Vector2Ref]) {.base.} =
- ## Changes shape type to `polygon`.
- ##
- ## Arguments:
- ## - `positions` is a varargs of polygon positions. Should be more than 2.
- self.shape_type = COLLISION_SHAPE_2D_POLYGON
- if positions.len() < 3:
- return
- self.polygon = @[]
- for i in positions:
- self.polygon.add(i)
- method draw*(self: CollisionShape2DPtr, w, h: GLfloat) =
- ## this method uses in the `window.nim`.
- {.warning[LockLevel]: off.}
- # Recalculate position.
- self.position = self.timed_position
- if self.centered:
- self.position = self.timed_position - self.rect_size/2
- else:
- self.position = self.timed_position
- self.calcGlobalPosition()
- # debug draw
- when defined(debug):
- let
- x = -w/2 + self.global_position.x
- y = h/2 - self.global_position.y
- if self.disable:
- glColor4f(0.5, 0.5, 0.5, 0.7)
- else:
- glColor4f(0.5, 0.6, 0.9, 0.7)
- case self.shape_type
- of COLLISION_SHAPE_2D_RECTANGLE:
- glRectf(x, y, x+self.rect_size.x, y-self.rect_size.y)
- of COLLISION_SHAPE_2D_CIRCLE:
- glBegin(GL_TRIANGLE_FAN)
- glVertex3f(x + self.x1, y - self.y1, self.z_index_global)
- for i in 0..180:
- let angle = PI2*i.float/180f
- glVertex3f(x + self.x1 + self.radius*cos(angle), y - self.y1 - self.radius*sin(angle), self.z_index_global)
- glEnd()
- of COLLISION_SHAPE_2D_POLYGON:
- glBegin(GL_POLYGON)
- for vec2 in self.polygon:
- glVertex3f(x + vec2.x, y - vec2.y, self.z_index_global)
- glEnd()
- method duplicate*(self: CollisionShape2DPtr, obj: var CollisionShape2DObj): CollisionShape2DPtr {.base.} =
- ## Duplicates CollisionShape2D object and create a new CollisionShape2D pointer.
- obj = self[]
- obj.addr
- method getGlobalMousePosition*(self: CollisionShape2DPtr): Vector2Ref {.inline.} =
- ## Returns mouse position.
- Vector2Ref(x: last_event.x, y: last_event.y)
- method isCollide*(self: CollisionShape2DPtr, x, y: float): bool =
- ## Checks collision with point.
- ##
- ## Arguments:
- ## - `x` is a point position at X axis.
- ## - `y` is a point position at Y axis.
- self.calcGlobalPosition()
- if self.disable:
- return false
- case self.shape_type
- of COLLISION_SHAPE_2D_RECTANGLE:
- return Rect2(self.global_position, self.rect_size).contains(x, y)
- of COLLISION_SHAPE_2D_CIRCLE:
- let
- dx = x - self.x1
- dy = y - self.y1
- return dx*dx + dy*dy <= self.radius*self.radius
- of COLLISION_SHAPE_2D_POLYGON:
- result = false
- var next = 1
- let length = self.polygon.len()
- for i in 0..<length:
- inc next
- if next == length: next = 0
- let
- a = self.polygon[i] + self.global_position
- b = self.polygon[next] + self.global_position
- if ((a.y >= y and b.y < y) or (a.y < y and b.y >= y)) and (x < (b.x-a.x)*(y-a.y) / (b.y-a.y)+a.x):
- result = not result
- method isCollide*(self: CollisionShape2DPtr, vec2: Vector2Ref): bool =
- ## Checks collision with point.
- self.calcGlobalPosition()
- if self.disable:
- return false
- case self.shape_type
- of COLLISION_SHAPE_2D_RECTANGLE:
- return Rect2(self.global_position, self.rect_size).contains(vec2)
- of COLLISION_SHAPE_2D_CIRCLE:
- let
- dx = vec2.x - self.x1
- dy = vec2.y - self.y1
- return dx*dx + dy*dy <= self.radius*self.radius
- of COLLISION_SHAPE_2D_POLYGON:
- result = false
- var next = 1
- let length = self.polygon.len()
- for i in 0..<length:
- inc next
- if next == length: next = 0
- let
- a = self.polygon[i] + self.global_position
- b = self.polygon[next] + self.global_position
- if ((a.y >= vec2.y and b.y < vec2.y) or (a.y < vec2.y and b.y >= vec2.y)) and (vec2.x < (b.x-a.x)*(vec2.y-a.y) / (b.y-a.y)+a.x):
- result = not result
- method isCollide*(self, other: CollisionShape2DPtr): bool {.base.} =
- ## Checks collision with other CollisionShape2D object.
- self.calcGlobalPosition()
- other.calcGlobalPosition()
- # Return false, if collision shape is disabled.
- if self.disable:
- return false
- elif other.disable:
- return false
- case self.shape_type
- of COLLISION_SHAPE_2D_RECTANGLE:
- case other.shape_type:
- of COLLISION_SHAPE_2D_RECTANGLE:
- return Rect2(other.global_position, other.rect_size).intersects(Rect2(self.global_position, self.rect_size))
- of COLLISION_SHAPE_2D_CIRCLE:
- return Rect2(self.global_position, self.rect_size).isCollideWithCircle(
- other.global_position.x + other.x1,
- other.global_position.y + other.y1, other.radius)
- of COLLISION_SHAPE_2D_POLYGON:
- var
- rect = Rect2(self.global_position, self.rect_size)
- next = 1
- let length = other.polygon.len()
- for i in 0..<length:
- inc next
- if next == length: next = 0
- let
- a = other.polygon[i] + other.global_position
- b = other.polygon[next] + other.global_position
- if rect.contains(a, b):
- return true
- of COLLISION_SHAPE_2D_CIRCLE:
- case other.shape_type:
- of COLLISION_SHAPE_2D_CIRCLE:
- let
- sradii = self.radius + other.radius
- dx = (other.global_position.x + other.x1) - (self.global_position.x + self.x1)
- dy = (other.global_position.y + other.y1) - (self.global_position.y + self.y1)
- return dx*dx + dy*dy <= sradii*sradii
- of COLLISION_SHAPE_2D_RECTANGLE:
- return Rect2(other.global_position, other.rect_size).isCollideWithCircle(
- self.global_position.x + self.x1,
- self.global_position.y + self.y1, self.radius)
- of COLLISION_SHAPE_2D_POLYGON:
- var
- circle = Circle2(self.global_position.x + self.x1, self.global_position.y + self.y1, self.radius)
- next = 1
- let length = other.polygon.len()
- for i in 0..<length:
- inc next
- if next == length: next = 0
- let
- a = other.polygon[i] + other.global_position
- b = other.polygon[next] + other.global_position
- if circle.contains(a, b):
- return true
- return false
- of COLLISION_SHAPE_2D_POLYGON:
- case other.shape_type:
- of COLLISION_SHAPE_2D_RECTANGLE:
- var
- rect = Rect2(other.global_position, other.rect_size)
- next = 1
- let length = self.polygon.len()
- for i in 0..<length:
- inc next
- if next == length: next = 0
- let
- a = self.polygon[i] + self.global_position
- b = self.polygon[next] + self.global_position
- if rect.contains(a, b):
- return true
- of COLLISION_SHAPE_2D_POLYGON:
- let
- length = self.polygon.len()
- otherlength = other.polygon.len()
- var next = 0
- for i in 0..<length:
- inc next
- if next == length: next = 0
- let
- a = self.polygon[i] + self.global_position
- b = self.polygon[next] + self.global_position
- var othernext = 0
- for i in 0..<otherlength:
- inc othernext
- if othernext == otherlength: othernext = 0
- let
- c = other.polygon[i] + other.global_position
- d = other.polygon[othernext] + other.global_position
- if intersects(a, b, c, d):
- return true
- of COLLISION_SHAPE_2D_CIRCLE:
- var
- circle = Circle2(other.global_position.x + other.x1, other.global_position.y + other.y1, other.radius)
- next = 1
- let length = self.polygon.len()
- for i in 0..<length:
- inc next
- if next == length: next = 0
- let
- a = self.polygon[i] + self.global_position
- b = self.polygon[next] + self.global_position
- if circle.contains(a, b):
- return true
- return false
|