I’ve been less active on here because I’ve been writing quite a bit of code. Like I said previously, I’m in the main swing of developing this game. This is where things start to get crazy. Namely, I’ve added quite a bit of classes to the project to handle everything from basic collisions and 3D math extensions to generating and displaying 3D text on the screen. I feel like (despite my previous post regarding Swift) things are keeping organized quite well and I haven’t strayed too far from my original architecture plan. I’ve spent most of my time working on the PracticeGameStateRunner class which runs the “Target Practice” initial level in the game. This mini level serves as a point for the player to learn the very simple controls and game mechanics of the game… and it’s serving excellently as a sandbox for me to test these all during development as well.
I’ll cover just a few pieces of code today to show the changes that I’ve made regarding the game State Runner protocol, and some cool stuff that I’ve been able to do with “smart” enums in swift. There’s a lot more I can talk about that I don’t want this post to go on forever.
Some Code
State runner protocol. Now I have some stuff in there to quickly respond to “game controller” and mouse events, all stemming from the game loop class.
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 |
//
// GameState.swift
// Gangster Driveby
//
// Created by Mike Farrell on 10/15/14.
// Copyright (c) 2014 Mike Farrell. All rights reserved.
//
import Foundation
protocol GameStateRunner
{
func processLogic(#delta: Float)
func processEvents()
func render()
func keyDown(keysym: SDL_Keycode)
func keyUp(keysym: SDL_Keycode)
func mouseMoved(event: SDL_MouseMotionEvent)
func mouseDown(event: SDL_MouseButtonEvent)
func mouseUp(event: SDL_MouseButtonEvent)
func joystickDown(controllerButton button: GangsterGameLoop.ControllerButton)
func joystickUp(controllerButton button: GangsterGameLoop.ControllerButton)
} |
Mesh Line Collider – a badly needed construct to determine whether or not a bullet-trajectory would intersect a polygonal mesh in the scene or not (and if so where). I tried porting this over to ObjC before swift and it was a nitemare. Swift’s version is definitely simpler thanks to operator overloading and multiple return values. Note the unsafe pointer craziness in swift which is considerably easier to deal with in C. (ported from http://geomalgorithms.com/a06-_intersect-2.html)
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 |
//
// MeshLineCollider.swift
// Gangster Driveby
//
// Created by Mike Farrell on 10/30/14.
// Copyright (c) 2014 Mike Farrell. All rights reserved.
//
import Foundation
class MeshLineCollider
{
weak var entity: Entity!
var boundingBoxCollider: MeshLineCollider?
let verts: GrowableArray
let faceIndices: GrowableArray
init(meshEntity: Entity)
{
entity = meshEntity
verts = entity.editVerts.mutableCopy() as GrowableArray
entity.applyTransformToVerts(verts, andNorms: nil)
let s: SubMesh = entity.submeshes[0] as SubMesh
faceIndices = s.editInds.mutableCopy() as GrowableArray
generateBoundingBoxCollider()
}
init(verts: GrowableArray, faces: GrowableArray)
{
self.verts = verts
faceIndices = faces
boundingBoxCollider = nil
}
convenience init(var verts: [float3], var faces: [uint3])
{
let vBuffer = UnsafeMutableBufferPointer(start: &verts, count: verts.count)
let vgArray = GrowableArray(data: vBuffer.baseAddress, ofSize: Int32(verts.count), andElementSize:Int32(sizeof(float3)), andExtraCapacity: 0)
let fBuffer = UnsafeMutableBufferPointer<uint3>(start: &faces, count: faces.count)
let fArray = GrowableArray(data: fBuffer.baseAddress, ofSize: Int32(faces.count*3), andElementSize:Int32(sizeof(uint)), andExtraCapacity: 0)
self.init(verts: vgArray, faces: fArray)
}
private final func split(quad: uint4) -> (uint3, uint3)
{
return (uint3(x: quad.x, y: quad.y, z: quad.w), uint3(x: quad.w, y: quad.y, z: quad.z))
}
private func generateBoundingBoxCollider()
{
var bbSize = float3.zero
entity.getBBox(&bbSize)
let box = Box3D(dimensions: bbSize, andPosition: float3.zero)
let boxVerts = box.generateVertices()
entity.applyTransformToVerts(boxVerts, andNorms: nil)
var boxInds: [uint4] = [ ]
boxInds.append(uint4(x: 0, y: 1, z: 2, w: 3))
boxInds.append(uint4(x: 0, y: 4, z: 5, w: 1))
boxInds.append(uint4(x: 2, y: 6, z: 7, w: 3))
boxInds.append(uint4(x: 3, y: 7, z: 4, w: 0))
boxInds.append(uint4(x: 1, y: 5, z: 6, w: 2))
boxInds.append(uint4(x: 4, y: 5, z: 6, w: 7))
var triangles: [uint3] = [ ]
for quad in boxInds
{
let spl = split(quad)
triangles.append(spl.0)
triangles.append(spl.1)
}
let fBuffer = UnsafeMutableBufferPointer<uint3>(start: &triangles, count: triangles.count)
let fArray = GrowableArray(data: fBuffer.baseAddress, ofSize: Int32(triangles.count*3), andElementSize:Int32(sizeof(uint)), andExtraCapacity: 0)
boundingBoxCollider = MeshLineCollider(verts: boxVerts, faces: fArray)
}
func debugRender()
{
if boundingBoxCollider != nil
{
boundingBoxCollider?.debugRender()
return
}
glDisable(GL_CULL_FACE.e)
glDepthRange(0.0, 0.9999);
let vgl = VertoGLStateMachine.currentMachine()
vgl.vertexPointerOfSize(3, type: GL_FLOAT.e, stride: 0, pointer: verts.data, bufferLength: GLsizeiptr(Int32(sizeof(float3))*verts.count))
vgl.setPrimaryColor(makeFloat4(0.2, 0.2, 0.2, 1))
vgl.prepareToDraw()
vgl.drawElementsInMode(GL_TRIANGLES.e, andCount: GLsizei(faceIndices.count), type: GL_UNSIGNED_INT.e, indexPointer: faceIndices.data,
bufferLength: GLsizeiptr(Int32(sizeof(uint))*faceIndices.count))
glDepthRange(0.0, 1.0);
}
//MARK: - Collision with line
func rayIntersection(rayOrigin ro: float3, rayDirection rd: float3, inout intersectionPoint: float3) -> IntersectionPoint?
{
if let bbox = boundingBoxCollider
{
if bbox.rayIntersection(rayOrigin: ro, rayDirection: rd, intersectionPoint: &intersectionPoint) == nil
{
return nil
}
}
let pointSet = IntersectionPointSet()
for i in 0..<faceIndices.count/3
{
let result = triangleIntersection(triangleIndex: Int(i), rayOrigin: ro, rayDirection: rd, intersectionPoint: &intersectionPoint)
if result.0
{
pointSet.add(IntersectionPoint(distance: result.1, point: intersectionPoint))
}
}
if !pointSet.empty()
{
return pointSet.nearest()
}
return nil
}
private func triangleIntersection(triangleIndex i: Int, rayOrigin ro: float3, rayDirection rd: float3, inout intersectionPoint ip: float3) -> (Bool, Float)
{
let smallNum: Float = 0.00000001
var u: float3, v: float3, n: float3 // triangle vectors
var dir: float3, w0: float3, w: float3 // ray vectors
var r: Float, a: Float, b: Float // params to calc ray-plane intersect
let vData = UnsafeMutableBufferPointer<float3>(start: UnsafeMutablePointer<float3>(verts.data), count: Int(verts.count))
let iData = UnsafeMutableBufferPointer<uint3>(start: UnsafeMutablePointer<uint3>(faceIndices.data), count: Int(faceIndices.count/3))
let v0 = vData[Int(iData[i].x)], v1 = vData[Int(iData[i].y)], v2 = vData[Int(iData[i].z)]
// get triangle edge vectors and plane normal
u = v1 - v0
v = v2 - v0
n = u.cross(v) // cross product
if n == float3.zero // triangle is degenerate
{
// do not deal with this case
return (false, 0)
}
dir = rd
w0 = ro - v0
a = -n.dot(w0)
b = n.dot(dir)
if fabsf(b) < smallNum
{
return (false, 0)
}
// get intersect point of ray with triangle plane
r = a / b
if r < 0.0 // ray goes away from triangle
{
return (false, 0) // => no intersect
}
// for a segment, also test if (r > 1.0) => no intersect
// intersect point of ray and plane
ip = ro + dir*r
// is I inside T?
var uu: Float, uv: Float, vv: Float, wu: Float, wv: Float, D: Float
uu = u.dot(u)
uv = u.dot(v)
vv = v.dot(v)
w = ip - v0
wu = w.dot(u)
wv = w.dot(v)
D = uv * uv - uu * vv
// get and test parametric coords
var s: Float, t: Float
s = (uv * wv - vv * wu) / D
if s < 0.0 || s > 1.0 // I is outside T
{
return (false, 0)
}
t = (uv * wu - uu * wv) / D
if t < 0.0 || (s + t) > 1.0 // I is outside T
{
return (false, 0)
}
return (true, r) // I is in T
}
} |
Cool little WIP controller button enum nested in the game loop class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
enum ControllerButton: Int
{
case Shoot = 9
case Aim = 8
var name: String
{
let buttonNames: [ControllerButton: String] = [ .Shoot: "Shoot", .Aim: "Aim" ]
return buttonNames[self]!
}
var obj: NSNumber
{
return NSNumber(integer: self.rawValue)
}
} |