Okay, so I have not done any game dev for a few months or so now. Recently came back to these forums and seeing all of the cool stuff you folks are making got me interested in doing some game dev again.
Tonight I started on creating a simple old school style FPS game.
And when I say old school style I am talking about the very old ones that uses keyboard controls only not mouse and keyboard.
This is obviously very early on but it feels right so far.
There is no texture mapping... just solid colored walls... but like the games of old that used color cycling creating colored bands to simulate 3D I use this FX on the floor, ceiling and walls even though everything really is 3D. Because it strengthens the 3D movement FX and again it connects it more to those old school games I enjoyed so much.
There is no collision checking yet. When you run the project just "pretend" collision checking is in.
The project is attached to this post. It is tiny just 8 KB. There is no media. Just one main file which I will also include right here.
// ***************************************************************************
// Project: MageQuest
// Created: 2018-02-01
// ***************************************************************************
SetErrorMode(2)
#constant KEY_LEFT 37
#constant KEY_A 65
#constant KEY_RIGHT 39
#constant KEY_D 68
#constant KEY_UP 38
#constant KEY_W 87
#constant KEY_DOWN 40
#constant KEY_S 83
global wallCubeTypes as integer[3]
global floorCubeTypes as integer[3]
global ceilCubeTypes as integer[3]
global worldmap as integer[2,19,19] // 0 = floor 1 = walls 2 = ceiling might not even use this but for now I am doing it this way... figure it all out as I go along
InitDisplay("MageQuest3D Created using App Game Kit Version 2", 1280, 720, 63)
CreateWallCubeTypes()
CreateFloorCubeTypes()
CreateCeilingCubeTypes()
SetSunActive(0)
SetAmbientColor(185,170,160)
SetCameraPosition(1, 3,2,0)
SetCameraLookAt(1, GetCameraX(1), GetCameraY(1), GetCameraZ(1) + 2, 0)
SetCameraFOV(1, 82)
//CreatePointLight(1, 1.5,1.9,3, 2, 1000,1000,1000)
DefineMap()
global playerMoveTimer as float
global playerTurnTimer as float
global sumFPS as float
global averageFPS as integer
global fpsCounter as integer
global FrameTimeScale as float
global FTS as float
global thisFrame as float
global lastFrame as float
local frames as integer
// allow display and everything to "settle" (stuff pushed into video card, etc) before getting onto the fun
sync()
sleep(500)
do
CalcFrameScale()
PlayerUpdate()
Inc sumFPS, floor(ScreenFPS())
Inc fpsCounter, 1
if fpsCounter > 119
averageFPS = sumFPS / 120
sumFPS = 0
fpsCounter = 0
endif
Print("FPS:" + str(averageFPS))
Print("ZP=" + mid(str(GetCameraX(1)), 1, 6))
Print("ZP=" + mid(str(GetCameraZ(1)), 1, 6))
Print("YR=" + str(floor(GetCameraAngleY(1))))
Sync()
loop
end
// this is just a handy function I wrote when I first start with AppGameKit 2 and use in all of my projects to make it quick and easy to handle retro style games with low resolutions.
function InitDisplay(title as string, width as integer, height as integer, maxFrameRate as float)
// set window properties
SetWindowTitle(title)
SetWindowSize(width, height, 0)
// set display properties
SetVirtualResolution(width, height)
SetOrientationAllowed(1, 1, 1, 1)
SetScissor(0,0,0,0)
UseNewDefaultFonts(1)
SetSyncRate(maxFrameRate, 0)
// SetVSync(1)
SetViewZoom(height / 144)
endfunction
// just my timing code I use milliseconds because this also works in HTML5 builds
function CalcFrameScale()
if lastFrame >= 0
thisFrame = GetMilliseconds()
FrameTimeScale = (thisFrame - lastFrame) / 16.5 //16.667
millis = thisFrame - lastFrame
if millis < 14
Sleep(2) // we have plenty of time might as well give some back
thisFrame = GetMilliseconds()
FrameTimeScale = (thisFrame - lastFrame) / 16.5 //16.667
endif
// limit the scale factor to prevent crazy delta updates from other processes stealing time, clicking on window title bar, etc
if FrameTimeScale > 3 then FrameTimeScale = 3
lastFrame = thisFrame
else
lastFrame = GetMilliseconds()
FrameTimeScale = 1
endif
// Just a short version of the same thing... less typing for all the places this will be used.
FTS = FrameTimeScale
endfunction
// **********************************************************************
// MAP BLOCK TEMPLATE CREATION
// Eventually these may be textured images... not sure yet...
// not really needed... right now it is very old school like with solid colors
// there are shades of each color which allows some banding to again strengthen
// the feel of moving and the old school look
// **********************************************************************
function CreateFloorCubeTypes()
floorCubeTypes[0] = CreateObjectBox(2,2,2)
SetObjectColor(floorCubeTypes[0], 112,112,112, 255)
SetObjectVisible(floorCubeTypes[0], 0)
floorCubeTypes[1] = CreateObjectBox(2,2,2)
SetObjectColor(floorCubeTypes[1], 116,116,116, 255)
SetObjectVisible(floorCubeTypes[1], 0)
floorCubeTypes[2] = CreateObjectBox(2,2,2)
SetObjectColor(floorCubeTypes[2], 120,120,120, 255)
SetObjectVisible(floorCubeTypes[2], 0)
endfunction
function CreateWallCubeTypes()
wallCubeTypes[0] = CreateObjectBox(2,2,2)
SetObjectColor(wallCubeTypes[0], 40,45,40, 255)
SetObjectVisible(wallCubeTypes[0], 0)
wallCubeTypes[1] = CreateObjectBox(2,2,2)
SetObjectColor(wallCubeTypes[1], 45,50,45, 255)
SetObjectVisible(wallCubeTypes[1], 0)
wallCubeTypes[2] = CreateObjectBox(2,2,2)
SetObjectColor(wallCubeTypes[2], 50,55,50, 255)
SetObjectVisible(wallCubeTypes[2], 0)
endfunction
function CreateCeilingCubeTypes()
ceilCubeTypes[0] = CreateObjectBox(2,2,2)
SetObjectColor(ceilCubeTypes[0], 60,60,62, 255)
SetObjectVisible(ceilCubeTypes[0], 0)
ceilCubeTypes[1] = CreateObjectBox(2,2,2)
SetObjectColor(ceilCubeTypes[1], 63,63,65, 255)
SetObjectVisible(ceilCubeTypes[1], 0)
ceilCubeTypes[2] = CreateObjectBox(2,2,2)
SetObjectColor(ceilCubeTypes[2], 66,66,68, 255)
SetObjectVisible(ceilCubeTypes[2], 0)
endfunction
// **********************************************************************
// WORLD MAP CREATION
// **********************************************************************
function DefineMap()
local map as string
// define floor
/*
map = "01210121012101210121"
map = map + "10121012101210121012"
map = map + "21012101210121012101"
map = map + "12101210121012101210"
map = map + "01210121012101210121"
map = map + "10121012101210121012"
map = map + "21012101210121012101"
map = map + "12101210121012101210"
map = map + "01210121012101210121"
map = map + "10121012101210121012"
map = map + "21012101210121012101"
map = map + "12101210121012101210"
map = map + "01210121012101210121"
map = map + "10121012101210121012"
map = map + "21012101210121012101"
map = map + "12101210121012101210"
map = map + "01210121012101210121"
map = map + "10121012101210121012"
map = map + "21012101210121012101"
map = map + "12101210121012101210"
*/
map = "********************"
map = map + "*110011001100110011*"
map = map + "*110011001100110011*"
map = map + "*221100112211001122*"
map = map + "*221100112211001122*"
map = map + "*********11*********"
map = map + "*1221100*00*0011221*"
map = map + "*1221100*11*0011221*"
map = map + "*211001122221100112*"
map = map + "*2110011211*1100112*"
map = map + "*1221122*00*2211221*"
map = map + "*1221122*11*2211221*"
map = map + "*********22*********"
map = map + "*001210121112001122*"
map = map + "*00121012111*001122*"
map = map + "*11*******22*112211*"
map = map + "*22*22222*11*112211*"
map = map + "*11*22222*00*221100*"
map = map + "*00*22222211*221100*"
map = map + "********************"
CreateFloor(map)
// define walls
map = "01210121012101210121"
map = map + "1 0"
map = map + "2 1"
map = map + "1 2"
map = map + "0 1"
map = map + "101210121 012101210"
map = map + "2 0 1 1"
map = map + "1 1 2 2"
map = map + "0 1"
map = map + "1 1 2 0"
map = map + "2 2 1 1"
map = map + "1 1 0 2"
map = map + "012101210 121012101"
map = map + "1 0"
map = map + "2 2 1"
map = map + "1 0121012 1 2"
map = map + "0 1 1 0 1"
map = map + "1 2 0 1 0"
map = map + "2 1 2 1"
map = map + "10121012101210121012"
CreateWalls(map)
// define ceiling
map = "********************"
map = map + "*110011001100110011*"
map = map + "*110011001100110011*"
map = map + "*221100112211001122*"
map = map + "*221100112211001122*"
map = map + "*********11*********"
map = map + "*1221100*00*0011221*"
map = map + "*1221100*11*0011221*"
map = map + "*211001122221100112*"
map = map + "*2110011211*1100112*"
map = map + "*1221122*00*2211221*"
map = map + "*1221122*11*2211221*"
map = map + "*********22*********"
map = map + "*001210121112001122*"
map = map + "*00121012111*001122*"
map = map + "*11*******22*112211*"
map = map + "*22*22222*11*112211*"
map = map + "*11*22222*00*221100*"
map = map + "*00*22222211*221100*"
map = map + "********************"
CreateCeiling(map)
endfunction
function CreateFloor(floormap as string)
local x as integer
local z as integer
local i as integer
local c as string
local t as integer
i = 1
for z = 0 to 19
for x = 0 to 19
c = mid(floormap, i, 1)
if c <> " " and c <> "*"
t = val(c)
//t = mod(z, 3)
worldmap[0, x, z] = CloneObject(floorCubeTypes[t])
SetObjectPosition(worldmap[0, x, z], x*2, 0, (19-z)*2)
SetObjectVisible(worldmap[0, x, z], 1)
else
worldmap[0, x, z] = -1
endif
Inc i,1
next x
next z
endfunction
function CreateWalls(wallsmap as string)
local x as integer
local z as integer
local i as integer
local c as string
local t as integer
i = 1
for z = 0 to 19
for x = 0 to 19
c = mid(wallsmap, i, 1)
if c <> " "
t = val(c)
//t = mod(z, 3)
worldmap[1, x, z] = InstanceObject(wallCubeTypes[t])
SetObjectPosition(worldmap[1, x, z], x*2, 2, (19-z)*2)
SetObjectAlphaMask(worldmap[1, x, z], 0)
SetObjectVisible(worldmap[1, x, z], 1)
else
worldmap[1, x, z] = -1
endif
Inc i,1
next x
next z
endfunction
function CreateCeiling(ceilingmap as string)
local x as integer
local z as integer
local i as integer
local c as string
local t as integer
i = 1
for z = 0 to 19
for x = 0 to 19
c = mid(ceilingmap, i, 1)
if c <> " " and c <> "*"
t = val(c)
//t = mod(z, 3)
worldmap[2, x, z] = InstanceObject(ceilCubeTypes[t])
SetObjectPosition(worldmap[2, x, z], x*2, 4, (19-z)*2)
SetObjectVisible(worldmap[2, x, z], 1)
else
worldmap[2, x, z] = -1
endif
Inc i,1
next x
next z
endfunction
// **********************************************************************
// INPUT METHODS
// **********************************************************************
function InputRequestLeft()
result as integer
if GetRawKeyState(KEY_LEFT) then result = 1
if GetRawKeyState(KEY_A) then result = 1
endfunction result
function InputRequestRight()
result as integer
if GetRawKeyState(KEY_RIGHT) then result = 1
if GetRawKeyState(KEY_D) then result = 1
endfunction result
function InputRequestUp()
result as integer
if GetRawKeyState(KEY_UP) then result = 1
if GetRawKeyState(KEY_W) then result = 1
endfunction result
function InputRequestDown()
result as integer
if GetRawKeyState(KEY_DOWN) then result = 1
if GetRawKeyState(KEY_S) then result = 1
endfunction result
// **********************************************************************
// PLAYER METHODS
// **********************************************************************
function PlayerUpdate()
local angle as integer
// turning is on a timer and by distinct steps
// this is for two reasons... first because this is keyboard only and "choppy" distinct jumps makes it easier to aim the player
// second I am going for an old school feel from the very earliest keyboard control only 3D games
if InputRequestLeft()
Inc playerTurnTimer, (1 * FTS)
if playerTurnTimer > 6
RotateCameraLocalY(1, -15)
playerTurnTimer = 0
endif
elseif InputRequestRight()
Inc playerTurnTimer, (1 * FTS)
if playerTurnTimer > 6
RotateCameraLocalY(1, 15)
playerTurnTimer = 0
endif
// if they are not turning set timer so when they request to turn it is instant... for playability
else
playerTurnTimer = 6
endif
// again movement is on a timer and by distinct steps
// this is for the retro feel and also these steps make it so the map array can be used for collision checking with the walls
if InputRequestUp()
Inc playerMoveTimer, (1 * FTS)
if playerMoveTimer > 7
MoveCameraLocalZ(1, 0.5)
playerMoveTimer = 0
endif
elseif InputRequestDown()
Inc playerMoveTimer, (1 * FTS)
if playerMoveTimer > 7
MoveCameraLocalZ(1, -0.5)
playerMoveTimer = 0
endif
// if they are not moving set timer so when they request to turn it is instant... for playability
else
playerMoveTimer = 7
endif
endfunction
OH! I should mention this is just something I am dabbling on. I will work on it when I feel the urge to. Almost certainly will not be every day. But I will try to do some more on it sometime this weekend. All depends on what all else is going on.
TI/994a (BASIC) -> C64 (BASIC/PASCAL/ASM/Others) -> Amiga (AMOS/BLITZ/ASM/C/Gamesmith) -> DOS (C/C++/Allegro) -> Windows (C++/C#/Monkey X/GL Basic/Unity/Others)