UnderwareDESIGN

General => Competitions => CleverCoders => Topic started by: kevin on April 06, 2009, 08:59:26 AM

Title: Challenge #17 - Lode Runner Styled Character AI
Post by: kevin on April 06, 2009, 08:59:26 AM

(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)

Title: Re: Challenge #17 - Lode Runner Styled Character AI
Post by: kevin on April 20, 2009, 03:28:44 PM
 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)
Title: Re: Challenge #17 - Lode Runner Styled Character AI
Post by: slayer93 on July 06, 2009, 03:05:56 PM
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]