you could write a fairly simple game engine in not too long. my own engine which just finally broke 1000 lines of code only took me a month or so to write, and ive been working on it at a rather casual basis. just start with basic vector and matrix classes. perhaps a model class as well. its not hard to make a simple model format, just store raw vertex data in a file and load it, i used a simple connect the dots rutine for my lua engine which would draw a line from vert to vert right on down the list. you had to use

of redundant verts but it would render fairly fast. rather easy to implement.
you need to be able to do basic vector operations like add, subtract, multiply and divide. which is more than enough to get basic stuff going. also a matrix multiply function. i use a eulers to matrix function to generate the desired rotation for each frame and that gets multiplied by the matrix for the object->world space transform matrix to generate the new transform matrix for that object. ive overloaded my matrix mulotiply operator to take vectors as well which makes them easy to transform. i just loop through the vectors and transform each one.
then you need to do pixel and line draws. i used sdl for this as well as imput, but im sure theres an easyer way to get graphics. i just use software rendering through sdl. didnt bother accelerating it sence it was only a line engine. drawing lines isnt that hard but it can get pretty difficult sometimes to make an optimised rutine. youl want some timing, i just used sdl to check the system time each frame, subtract the time from the last fram and you get how long the frame took. use this when handeling physics.
ive only really had trouble dealing with dynamic memory and especially all the pointers. i wrote a vector based font system which is giving me a segmentation fault. im probibly trying to access a member of an array which doesnt exist. its sorta haulting my progress. so just stick with fixed arrays in your first attempt.
anyway heres the lua version, my c++ version is full of bugs and im none too browd of it yet. you need
luasdl to run it. just stick my code in __init__.lua and stick it in the same folder as luasdl.
-------------------------
-----init-----screen-----
-------------------------
SDL = SDL
SDL.SDL_Init(SDL.SDL_INIT_EVERYTHING)
screen = SDL.SDL_SetVideoMode(800, 600, 32, 0)
info = SDL.SDL_GetVideoInfo()
w,h = info.current_w, info.current_h
cx,cy = w/2,h/2
rect = SDL.SDL_Rect_local()
rect.x, rect.y = 0,0
rect.w, rect.h = w,h
grey = SDL.SDL_MapRGB(screen.format, 0x80, 0x80, 0x80)
blue = SDL.SDL_MapRGB(screen.format, 0x00, 0x00, 0x80)
---------------------------------------------
-----end init screen-----start type defs-----
---------------------------------------------
vec_2d = function(xy,ey)
local v = {}
if getmetatable(xy) == vec_2d_mt then
v.x = xy.x
v.y = xy.y
elseif type(xy) == 'table' then
v.x = xy[1]
v.y = xy[2]
elseif type(xy) == 'number' and type(ey) == 'number' then
v.x, v.y = xy, ey
else
v.x, v.y = 0, 0
end
setmetatable(v, vec_2d_mt)
return v
end
vec_2d_mt = {
__add = function(v1,v2) return vec_2d(v1.x + v2.x, v1.y + v2.y) end,
__sub = function(v1,v2) return vec_2d(v1.x - v2.x, v1.y - v2.y) end,
__unm = function(v) return vec_2d(-v.x,-v.y) end,
__mul = function(v1,v2)
if type(v1) == 'number' then
return vec_2d(v1 * v2.x, v1 * v2.y)
elseif type(v2) == 'number' then
return vec_2d(v1.x * v2, v1.y * v2)
else
return vec_2d(v1.x * v2.x, v1.y * v2.y)
end
end,
__div = function(v1,v2)
if type(v1) == 'number' then
return vec_2d(v1 / v2.x, v1 / v2.y)
elseif type(v2) == 'number' then
return vec_2d(v1.x / v2, v1.y / v2)
else
return vec_2d(v1.x / v2.x, v1.y / v2.y)
end
end,
__concat = function(v,r)
local nx = v.x * math.cos(r) - v.y * math.sin(r)
local ny = v.x * math.sin(r) + v.y * math.cos(r)
v = vec_2d( nx, ny )
return v
end,
__index = function(v,key)
if key == 'norm' then
local len = math.sqrt( v.x^2 + v.y^2 )
v.norm = vec_2d( v.x / len, v.y / len )
return v.norm
end
end
}
vec_3d = function(xyz,ey,ez)
local v = {}
if getmetatable(xyz) == vec_3d_mt then
v.x = xyz.x
v.y = xyz.y
v.z = xyz.z
elseif type(xyz) == 'table' then
v.x = xyz[1]
v.y = xyz[2]
v.z = xyz[3]
elseif type(xyz) == 'number' and type(ey) == 'number' and type(ez) == 'number' then
v.x, v.y, v.z = xyz, ey, ez
else
v.x, v.y, v.z = 0, 0, 0
end
setmetatable(v, vec_3d_mt)
return v
end
vec_3d_mt = {
__add = function(v1,v2) return vec_3d(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z) end,
__sub = function(v1,v2) return vec_3d(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z) end,
__unm = function(v) return vec_3d(-v.x,-v.y,-v.z) end,
__mul = function(v1,v2)
if type(v1) == 'number' then
return vec_3d(v1 * v2.x, v1 * v2.y, v1 * v2.z)
elseif type(v2) == 'number' then
return vec_3d(v1.x * v2, v1.y * v2, v1.z * v2)
else
return vec_3d(v1.x * v2.x, v1.y * v2.y, v1.z * v2.z)
end
end,
__div = function(v1,v2)
if type(v1) == 'number' then
return vec_3d(v1 / v2.x, v1 / v2.y, v1 / v2.z)
elseif type(v2) == 'number' then
return vec_3d(v1.x / v2, v1.y / v2, v1.z / v2)
else
return vec_3d(v1.x / v2.x, v1.y / v2.y, v1.z / v2.z)
end
end,
__index = function(v,key)
if key == 'norm' then
local len = math.sqrt( v.x^2 + v.y^2 + v.z^2 )
v.norm = vec_3d( v.x / len, v.y / len, v.z / len )
return v.norm
end
end
}
---------------------------------------
-----end type defs-----start maths-----
---------------------------------------
normalize_2d = function(v)
local len = math.sqrt( math.abs( v[1]*v[1] ) + math.abs( v[2]*v[2] ) )
v[1], v[2] = v[1] / len, v[2] / len
end
normalize_3d = function(v)
local len = math.sqrt( math.abs( v[1]*v[1] ) + math.abs( v[2]*v[2] ) + math.abs( v[3]*v[3] ) )
v[1], v[2], v[3] = v[1] / len, v[2] / len, v[3] / len
end
multmat = function(mat1,mat2) --multiply a couple 3x3 matricies
local mat3 = {}
mat3[1] = (mat1[1] * mat2[1]) + (mat1[2] * mat2[4]) + (mat1[3] * mat2[7])
mat3[2] = (mat1[1] * mat2[2]) + (mat1[2] * mat2[5]) + (mat1[3] * mat2[8])
mat3[3] = (mat1[1] * mat2[3]) + (mat1[2] * mat2[6]) + (mat1[3] * mat2[9])
mat3[4] = (mat1[4] * mat2[1]) + (mat1[5] * mat2[4]) + (mat1[6] * mat2[7])
mat3[5] = (mat1[4] * mat2[2]) + (mat1[5] * mat2[5]) + (mat1[6] * mat2[8])
mat3[6] = (mat1[4] * mat2[3]) + (mat1[5] * mat2[6]) + (mat1[6] * mat2[9])
mat3[7] = (mat1[7] * mat2[1]) + (mat1[8] * mat2[4]) + (mat1[9] * mat2[7])
mat3[8] = (mat1[7] * mat2[2]) + (mat1[8] * mat2[5]) + (mat1[9] * mat2[8])
mat3[9] = (mat1[7] * mat2[3]) + (mat1[8] * mat2[6]) + (mat1[9] * mat2[9])
return mat3
end
matxvec = function(mat,vec) --multiply a vextor by a matrix
local x = (mat[1] * vec[1]) + (mat[2] * vec[2]) + (mat[3] * vec[3])
local y = (mat[4] * vec[1]) + (mat[5] * vec[2]) + (mat[6] * vec[3])
local z = (mat[7] * vec[1]) + (mat[8] * vec[2]) + (mat[9] * vec[3])
vec[1],vec[2],vec[3] = x,y,z
return vec
end
rotnorm = function(norm,angs) --rotates a normal, in theory
local matx = {1,0,0,0,1,0,0,0,1}
local maty = {1,0,0,0,1,0,0,0,1} --create 3x3 matrix arrays for xyz and fill it in with an identity matrix
local matz = {1,0,0,0,1,0,0,0,1}
matx[5] = math.cos(angs[1])
matx[6] = math.sin(angs[1])
matx[8] = -math.sin(angs[1])
matx[9] = math.cos(angs[1])
maty[1] = math.cos(angs[2])
maty[3] = -math.sin(angs[2]) --fill in the nessicary parts of the matrix for each axis
maty[7] = math.sin(angs[2])
maty[9] = math.cos(angs[2])
matz[1] = math.cos(angs[3])
matz[2] = math.sin(angs[3])
matz[4] = -math.sin(angs[3])
matz[5] = math.cos(angs[3])
local mat = multmat(matx,maty) --multiply our matrixies
mat = multmat(mat,matz) --to get total matrix
norm = matxvec(mat,norm) --apply the matrix
return norm
end
rotpoint = function(norm,angs,axis) --point rotation
local mat = {}
local c = math.cos(angs)
local s = math.sin(angs)
local t = 1 - c
mat[1] = t * axis[1]^2 + c
mat[2] = t * axis[1] * axis[2] + s * axis[3]
mat[3] = t * axis[1] * axis[3] - s * axis[2]
mat[4] = t * axis[1] * axis[2] - s * axis[3]
mat[5] = t * axis[2]^2 + c
mat[6] = t * axis[2] * axis[3] + s * axis[1]
mat[7] = t * axis[1] * axis[3] + s * axis[2]
mat[8] = t * axis[2] * axis[3] - s * axis[1]
mat[9] = t * axis[3]^2 + c
if norm then --optionally return a matrix instead
norm = matxvec(mat,norm) --apply the matrix
return norm
else
return mat
end
end
---------------------------------------------
-----end maths-----start transformations-----
---------------------------------------------
transmodel = function(m, t)
for i=1, #m do
local pnt = m[i]
pnt[1] = pnt[1] + (t[1] or t.x)
pnt[2] = pnt[2] + (t[2] or t.y)
pnt[3] = pnt[3] + (t[3] or t.z)
m[i] = pnt
end
return m
end
rotmodel = function(m, r)
local posx = m[1][1]
local posy = m[1][2]
local posz = m[1][3]
m = transmodel(m, {-posx,-posy,-posz})
for i=1, #m do
if r[3] ~= 0 then m[i] = rotpoint(m[i], r[3], m[2]) end
if r[2] ~= 0 then m[i] = rotpoint(m[i], r[2], m[3]) end
if r[1] ~= 0 then m[i] = rotpoint(m[i], r[1], m[4]) end
end
m = transmodel(m, {posx,posy,posz} )
return m
end
rotscene = function(cmat, cpos, mods)
for i=1, #mods do
local tempmod = mods[i] --back up the model first
tempmod = transmodel(tempmod, {-cpos[1],-cpos[2],-cpos[3]})
for j=1, #tempmod do
matxvec(cmat, tempmod[j])
end
drawmodel(tempmod)
end
end
project = function(vec)
local X = cx + ( ((vec[1] or vec.x) / (vec[3] or vec.z)) * cx )
local Y = cy + (-((vec[2] or vec.y) / (vec[3] or vec.z)) * cy * (w/h) )
return X,Y
end
--------------------------------------------------
-----end transformations-----start rendering------
--------------------------------------------------
drawline = function(v1, v2)
local xd = v1.x - v2.x
local yd = v1.y - v2.y
local len = math.sqrt( (xd^2) + (yd^2) )
xd = xd/len
yd = yd/len
for i=1, len, 1 do
local x,y = v1.x - i*xd,v1.y - i*yd
if x > 0 and y > 0 and x < w and y < h then
SDL.SDL_PutPixel(screen, x,y , blue)
end
end
end
drawmodel = function(m)
SDL.SDL_LockSurface(screen)
for i=6, #m - 1, 1 do
--if (m[i][3] + m[i+1][3]) / 2 > 0 then
if m[i][3] > 0 and m[i+1][3] >0 then
drawline( vec_2d( project( m[i] ) ),vec_2d( project( m[i+1] ) ) )
end
end
SDL.SDL_UnlockSurface(screen)
end
----------------------------------------------------------
-----start rendering-----start declarations and calls-----
----------------------------------------------------------
model = { {0,0,0},{0,0,1},{0,1,0},{1,0,0},{0,0,5000}, ---heh cobra mk3, format is position, fvec, uvec, svec, laser, model
{0, 15, 0}, --index 5
{0, 15, -32.5},
{44, 10, -32.5},
{65, -3, -32.5},
{60, -3, -13}, --use 9 for gun 1
{16, -0.5, 32.5}, --index 10
{-16, -0.5, 32.5},
{-60, -3, -13}, --and 12 for gun 2
{-65, -3, -32.5},
{-44, 10, -32.5},
{0, 15, 0},
{44, 10, -32.5},
{60, -3, -13},
{16, -0.5, 32.5},
{0, 15, 0},
{-16, -0.5, 32.5},
{-60, -3, -13},
{-44, 10, -32.5},
{0, 15, -32.5},
{16, -15, -32.5},
{-16, -15, -32.5},
{-60, -3, -13},
{-65, -3, -32.5},
{-44, 10, -32.5},
{0, 15, -32.5},
{-16, -15, -32.5},
{-65, -3, -32.5},
{-16, -15, -32.5},
{-16, -0.5, 32.5},
{16, -0.5, 32.5},
{16, -15, -32.5},
{65, -3, -32.5},
{60, -3, -13},
{16, -15, -32.5}
}
campos = {0,0,0}
camrot = {1,0,0,0,1,0,0,0,1}
mata = {1,0,0,0,1,0,0,0,1}
matb = {1,0,0,0,1,0,0,0,1}
model = transmodel(model, {0,0,200} )
vel = {0,0,0}
rot = {0,0,0}
drift = vec_3d(0,0,0)
shot = 0
-------------------------
-----start main loop-----
-------------------------
repeat
local event = SDL.SDL_Event_local()
SDL.SDL_PollEvent(event)
local key = event.key.keysym.sym
if event.type == SDL.SDL_QUIT then kill = true end --check for killbox
if event.type == SDL.SDL_KEYDOWN then
if key == SDL.SDLK_KP_MINUS then joyon = true end
if key == SDL.SDLK_SPACE then fire = 1 end
if key == SDL.SDLK_KP_PERIOD then kill = true end
if key == SDL.SDLK_KP8 then rot[1] = -0.01 end
if key == SDL.SDLK_KP2 then rot[1] = 0.01 end
if key == SDL.SDLK_KP4 then rot[2] = 0.01 end
if key == SDL.SDLK_KP6 then rot[2] = -0.01 end
if key == SDL.SDLK_KP7 then rot[3] = -0.01 end
if key == SDL.SDLK_KP9 then rot[3] = 0.01 end
if key == SDL.SDLK_KP1 then vel[1] = -0.001 end
if key == SDL.SDLK_KP3 then vel[1] = 0.001 end
if key == SDL.SDLK_KP_PLUS then vel[2] = 0.001 end
if key == SDL.SDLK_KP_ENTER then vel[2] = -0.001 end
if key == SDL.SDLK_KP5 then vel[3] = 0.005 end
if key == SDL.SDLK_KP0 then vel[3] = -0.001 end
elseif event.type == SDL.SDL_KEYUP then
if key == SDL.SDLK_SPACE then fire = 0 end
if key == SDL.SDLK_KP8 or key == SDL.SDLK_KP2 then rot[1] = 0 end
if key == SDL.SDLK_KP4 or key == SDL.SDLK_KP6 then rot[2] = 0 end
if key == SDL.SDLK_KP7 or key == SDL.SDLK_KP9 then rot[3] = 0 end
if key == SDL.SDLK_KP1 or key == SDL.SDLK_KP3 then vel[1] = 0 end
if key == SDL.SDLK_KP_PLUS or key == SDL.SDLK_KP_ENTER then vel[2] = 0 end
if key == SDL.SDLK_KP0 or key == SDL.SDLK_KP5 then vel[3] = 0 end
end
if SDL.SDL_NumJoysticks() and joyon then
stick = SDL.SDL_JoystickOpen(0)
axes = SDL.SDL_JoystickNumAxes(stick)
balls = SDL.SDL_JoystickNumHats(stick)
print(balls)
axisx = SDL.SDL_JoystickGetAxis(stick, 0)
axisy = SDL.SDL_JoystickGetAxis(stick, 1)
axisz = SDL.SDL_JoystickGetAxis(stick, 2)
axisx2 = SDL.SDL_JoystickGetAxis(stick, 3)
axisy2 = SDL.SDL_JoystickGetAxis(stick, 4)
axisz2 = SDL.SDL_JoystickGetAxis(stick, 5)
axisx = axisx / 32768 --get sdl axes down to a -1 to 1 scale
axisy = axisy / 32768
axisz = -axisz / 32768
axisx2 = -axisx2 / 32768
axisy2 = axisy2 / 32768
axisz2 = axisz2 / 32768
if axes > 4 then vel[1] = axisy2 / 1000 end --check to make sure the axis exists before using it if no axis exists then use keyboard
if axes > 5 then vel[2] = axisz2 / 1000 end
if axes > 2 then vel[3] = (axisz + 0.333) / 333 end --throttle axis has 5x more forward than backward thrust
if axes > 1 then rot[1] = axisy / 100 end
if axes > 3 then rot[2] = axisx2 / 100 end
if axes > 0 then rot[3] = axisx / 100 end
fire = SDL.SDL_JoystickGetButton(stick, 0)
if SDL.SDL_JoystickGetButton(stick, 1) == 1 then --button 2 closes the stick and goes back to kb mode till kp- is hit again
joyon = false
SDL.SDL_JoystickClose(stick)
end
end
SDL.SDL_FillRect(screen, rect, grey)
muc = vec_3d( model[1] )
muf = vec_3d( model[2] )
muu = vec_3d( model[3] )
mus = vec_3d( model[4] )
meow = ( (mus - muc) * vel[1] )+( (muu - muc) * vel[2] )+( (muf - muc) * vel[3] )
drift = drift + meow
model = transmodel(model, drift)
model = rotmodel(model, {0.0,0.0,0.0})
mata = rotpoint(false, rot[1], {camrot[1],camrot[4],camrot[7]})
matb = rotpoint(false, rot[2], {camrot[2],camrot[5],camrot[8]})
mata = multmat(mata,matb)
matb = rotpoint(false, rot[3], {camrot[3],camrot[6],camrot[9]})
camrot = multmat(mata,matb)
--camrot = multmat(camrot, mata)
models = {model}
rotscene(camrot, campos, models)
--drawmodel(model)
if fire == 1 then
shot = math.random(-5,5)
if shot > 0 then drawline(vec_2d(project(model[10])),vec_2d(project(model[5]))) end
if shot < 0 then drawline(vec_2d(project(model[13])),vec_2d(project(model[5]))) end
end
SDL.SDL_UpdateRect(screen, 0, 0, 0, 0)
until kill ~= nil
mind you youre not controling the ship youre flying around it. keypad does everything, period exits. if you hit - tou should get joystick imput.