(http://www.underwaredesign.com/PlayBasicSig.png) (http://www.playbasic.com)
Challenge #17 - Lode Runner Styled Character AI
In keeping with our recent
retro game influenced challenges, this time around we'll up the anti and tackle the AI mechanics of a classic retro platform game called
Lode Runner. The focus here is not to built a remake of the game (there's dozens of those already), but derive an approach for making the opponent characters (we'll call them Bad Guys) chase after the Player ?
In the original Lode Runner, the player (and bad guys) have limited movement. They can run Left / Right, climb Up / Down ladders and fall. Interestingly they couldn't jump, so in order to avoid being caught, you either had to run away, or dig a hole in the ground and temporarily trap them. We won't worry about digging aspect (you can if you like). Lets just focus on how can we make the bad guy characters appear to chase and home in on the player.
So keep in mind, that both the Player & AI characters will need to be able to navigate the environment equally. Which means when a character (player or bad guy) is standing upon a ground tile, they're able to move left or right. If the character moves off the solid ground they begin falling until they land upon something solid. Either a ground tile or ladder tile. If the character is standing squarely over a ladder tile, the character has the option to climb up/down as well as. Optionally, you could limit the character to climbing (up down) until they reach the top/bottom of the ladder, or you could allow them move left/right and jump off the ladder which is how the original game works. If you jump off onto nothing then you begin falling.
Initially this might sound like a very complex challenge, but remember we're talking about replicating the logic used in a
25 year old game. So you might be surprised at how simple is actually is.
Apart from the AI, this challenge presents some good practice in building small environments and having characters navigate around them. So you will need a way the display a map on screen (no scrolling required). Graphically, it doesn't matter how pretty it looks, you just need something to represent the bricks and ladders. The characters can be animated if you like, but some simple blocks (stick men) would suffice.
It's not about the visuals, it's the game mechanics we're learning. So good luck.
Reference Materials http://en.wikipedia.org/wiki/Lode_Runner (http://en.wikipedia.org/wiki/Lode_Runner)
Submission * Submissions can be written in any version of PlayBasic (http://www.playbasic.com)
* Submission can only be accepted in
Source Code form only.
* Authors can use external Media (images/music) provided you created the media/and or have permission to distribute the includes media or the Media is in the public domain.
*
Zip up your Submissions and please try to keep it smaller than
500K !
*
Authors automatically give consent for their submission(s) to distributed and used for promotional purposes via UnderwareDesign.com, PlayBasic code tank.
* Authors can submit as many times as they like.
* To make a submission, zip up your projects source code + media and either post it in this thread, or create your own thread in either the Source Codes or Show Case forums and post a link bellow.
Dead Line Anytime before the next ice age
Prizes * The best submissions will periodically be highlighted through the PlayBasic IDE news service.
Return to Challenge Index (http://www.underwaredesign.com/forums/index.php?topic=3225.0)
Need help ?
Here's an example that contains a set Lode Runner tiles and a simple level editor for those wishing to participate in the this challenge.
The graphics and code we're created by Van B.
Lode Runner Graphics and Level Editor (http://www.underwaredesign.com/forums/index.php?topic=3021.0)
Thought I would give this a try :)
Everything is really basic though which caused a few problems (nothing that should be to much of a bother though).
I also hope that it is similar with the actually game because I've never played it.
It uses a VanB's editor and media (Thanks!) so really all that is included is a level I made and his tiles.
Edit: I decided to fix the player movement a bit and did a distance check for chasing the player (You can just replace the updated code or redownload).
Code if you just want to look at it
[pbcode]
; PROJECT : Lode Runner AI
; AUTHOR : Christian Ang a.k.a Slayer93
; CREATED : 7/6/2009
; EDITED : 7/6/2009
; ---------------------------------------------------------------------
#include "maps"
;
; Setup
OpenScreen 800,600,32,1
SetFps 60
Mouse False
; Setup Camera
CreateCamera 1
RenderToScreen
; Load the level
TileSet=loadNewImage("loderunner.png")
; Get a Player and Bad Guy Image
DrawImage TileSet, 0, 0, 0
Global bgImg=GetFreeImage()
GetImage bgImg, 0, 24, 24, 48
Global plrImg=GetFreeImage()
GetImage plrImg, 0, 72, 24, 96
level=NewMap(1)
MakeMapGfx level,TileSet,24,24,40,rgb(0,0,0)
LoadLevel("level.map",level,1)
; Ues this to store tile number but not player or bad guy
Dim MapData(31,20)
; Store entity here
Type tEntity
spr as integer
state as integer
substate as integer
time as integer
endtime as integer
xPos as integer
yPos as integer
EndType
; Bad Guys Array
Dim badGuys(0) as tEntity
; Player Data
Dim Player as tEntity
; Ready Map - basically run through the map to see what is what
; and then store the information in an array to move the bad guys
; check where things can move, etc.
ReadyMap(level,1)
;
; Main Loop
Do
; Clear and Capture gfx
CaptureToScene
ClsScene
; Control AI
ControlAI()
; Control Player
ControlPlayer()
; Draw gfx
DrawAllSprites
DrawMap level,1,0,0
DrawCamera 1
; Sync
Sync
Loop
; The Ai of the bad guys is going to be split into 2 states
; State 0 - Roaming on the platform it is on (further split into left/right/idle)
; State 1 - Chase player (meaning it can see him/her)
Function ControlAI()
For i = 1 to GetArrayElements(BadGuys(),1)
; Roam
If BadGuys(i).state = 0
; We don't want them to change the direction every frame so setup a timer
If Timer()-BadGuys(i).time = Timer() or Timer()-BadGuys(i).time > BadGuys(i).endtime
BadGuys(i).substate = rnd(2) ` 0 - idle 1 - left 2 - right
BadGuys(i).endtime = rnd(3000)+1000 ` At least 1 second and at most 4
BadGuys(i).time = Timer()
Endif
If BadGuys(i).substate = 0 `somewhat idle
; Do nothing (except if their on a ladder then move them)
If GetMapData(BadGuys(i).xPos,BadGuys(i).yPos) = 5 or GetMapData(BadGuys(i).xPos,BadGuys(i).yPos) = 101
If GetMapData(BadGuys(i).xPos-24,BadGuys(i).yPos) = 0
BadGuys(i).xPos=BadGuys(i).xPos+1
MoveSprite BadGuys(i).spr,1,0
Else
BadGuys(i).xPos=BadGuys(i).xPos-1
MoveSprite BadGuys(i).spr,-1,0
Endif
Endif
Else
If BadGuys(i).substate = 1 `left
; Make sure it didn't move to much
If GetMapData(BadGuys(i).xPos-1,BadGuys(i).yPos) => 100 or GetMapData(BadGuys(i).xPos-1,BadGuys(i).yPos) = 5
BadGuys(i).xPos=BadGuys(i).xPos-1
MoveSprite BadGuys(i).spr,-1,0
Else
BadGuys(i).time = 0
Endif
Else
If BadGuys(i).substate = 2 `right
; Make sure it didn't move to much
If GetMapData(BadGuys(i).xPos+25,BadGuys(i).yPos) => 100 or GetMapData(BadGuys(i).xPos+25,BadGuys(i).yPos) = 5
BadGuys(i).xPos=BadGuys(i).xPos+1
MoveSprite BadGuys(i).spr,1,0
Else
BadGuys(i).time = 0
Endif
Endif
Endif
Endif
; Check if there is a player in sight to chase
; Check if he is close to chase (on same level and not to far)
If BadGuys(i).yPos > Player.yPos-12 and BadGuys(i).yPos < Player.yPos+12 or abs(BadGuys(i).xPos-Player.xPos) > 100
BadGuys(i).state = 1
Endif
Else
; Chase
If BadGuys(i).state = 1
; Go towards player
If BadGuys(i).xPos > Player.xPos
If GetMapData(BadGuys(i).xPos-1,BadGuys(i).yPos) => 100 or GetMapData(BadGuys(i).xPos-1,BadGuys(i).yPos) = 5
BadGuys(i).xPos=BadGuys(i).xPos-1
MoveSprite BadGuys(i).spr,-1,0
Endif
Else
If GetMapData(BadGuys(i).xPos+25,BadGuys(i).yPos) => 100 or GetMapData(BadGuys(i).xPos+25,BadGuys(i).yPos) = 5
BadGuys(i).xPos=BadGuys(i).xPos+1
MoveSprite BadGuys(i).spr,1,0
Endif
Endif
; Check if he is out of sight or to far
If BadGuys(i).yPos < Player.yPos-12 or BadGuys(i).yPos > Player.yPos+12 or abs(BadGuys(i).xPos-Player.xPos) > 100
BadGuys(i).state = 0
Endif
Endif
Endif
Next i
EndFunction
Function ControlPlayer()
If UpKey()=1
; Check if next space can be moved into
If GetMapData(Player.xPos+8,Player.yPos+23) = 5 or GetMapData(Player.xPos+16,Player.yPos+23) = 5
MoveSprite Player.spr,0,-1
Player.yPos=Player.yPos-1
ExitFunction
Endif
Endif
If DownKey()=1
; Check if next space can be moved into
If GetMapData(Player.xPos+8,Player.yPos+25) = 5 or GetMapData(Player.xPos+16,Player.yPos+25) = 5
MoveSprite Player.spr,0,1
Player.yPos=Player.yPos+1
ExitFunction
Endif
Endif
If LeftKey()=1
; Check if next space can be moved into
i = GetMapData(Player.xPos-1,Player.yPos+23)
If i => 100 or i = 5 or i = 0
i = GetMapData(Player.xPos-1,Player.yPos)
If i => 100 or i = 5 or i = 0
MoveSprite Player.spr,-1,0
Player.xPos=Player.xPos-1
Endif
Endif
Endif
If RightKey()=1
; Check if next space can be moved into
i = GetMapData(Player.xPos+25,Player.yPos+23)
If i => 100 or i = 5 or i = 0
i = GetMapData(Player.xPos+25,Player.yPos)
If i => 100 or i = 5 or i = 0
MoveSprite Player.spr,1,0
Player.xPos=Player.xPos+1
Endif
Endif
Endif
; Fake Gravity
If GetMapData(Player.xPos+8,Player.yPos) = 0 and GetMapData(Player.xPos+16,Player.yPos+24) = 0 or GetMapData(Player.xPos+16,Player.yPos+24) => 100
MoveSprite Player.spr,0,1
Player.yPos=Player.yPos+1
Endif
EndFunction
Function GetMapData(xPos,yPos)
For y = 0 to 20
For x = 0 to 31
If xPos => x*24 and xPos < x*24+24 and yPos => y*24 and yPos < y*24+24
i = MapData(x,y)
ExitFunction i
Endif
Next x
Next y
EndFunction -1
; Ready Map Function
Function ReadyMap(Map, Level)
For y = 0 to 20
For x = 0 to 31
tile=PeekLevelTile(Map, Level, x, y)
; If bad guy make room for him
If tile = 8
i=GetFreeCell(badGuys())
badGuys(i).spr=NewSprite(x*24,y*24,bgImg)
badGuys(i).xPos=x*24
badGuys(i).yPos=y*24
; Pop bad guy from map to draw seperately
PokeLevelTile Map, Level, x, y, 0
Else
; If player store data
If tile = 24
Player.spr=NewSprite(x*24,y*24,plrImg)
Player.xPos=x*24
Player.yPos=y*24
; Pop player from map to draw seperately
PokeLevelTile Map, Level, x, y, 0
Else
; Its part of the map so store data in MapData array
MapData(x,y) = tile
; Check if it is a floor and store a value above tile
; to show it is something they can walk on
If MapData(x,y) = 1
If MapData(x,y-1) = 0 then MapData(x,y-1) = 100
Endif
If MapData(x,y) = 5
If MapData(x,y-1) = 0 then MapData(x,y-1) = 101
Endif
Endif
Endif
Next x
Next y
EndFunction
[/pbcode]