Building Vic20 Shamus In PlayBASIC (Blog)

Started by kevin, January 11, 2019, 11:06:35 AM

Previous topic - Next topic

kevin

#15
RCMVD -  Shamus  - Design Documents / Idea Pad / Brain Storming  (2019-02-17)

  in this video we take a step back from programming and have a look at what things we'll actually need to complete our objective.  What is the objective ?  It's simply to build a working display and game engine that can recreate Vic20 Shamus playing in PlayBASIC.  

  This is a long video



Video





Download

  attached bellow.


kevin

#16
  RCMVD -  Shamus  - ROOMS and Map parsing  (2019-02-24)
 
   Just getting stuck into writing the rooms and map parsing code,  which will look a little like the example bellow.   So far it's mostly a cut'n'paste existing sprite loading into the format we need.    Looking at it, you couold write a generic loading of sorts to handle all the files.  Since the form is basically the same





;----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
;----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
;----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
;                                     SHAMUS ROOM DEF'S
;----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
;----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
;----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


;  Room render instructions
;  [MAPRENDER]
; CLS BlockName,ColourName

; HLINECOLOUR ColourName ; Colour name from VIC 16 colour PALETTE
; VLINECOLOUR ColourName ; Colour name from VIC 16 colour palette

; HLINE TileName,X1,Y1,Size ; Draw horizontal line of tiles
; VLINE TileName,X1,Y1,Size ; Draw vertical line of tiles



[room]
number=0
name=Starting Room

[MapRender]

Cls("BLACK","BLACK")
HLINECOLOUR("YELLOW")
VLINECOLOUR("CYAN")

HLine("hwall2by2",6,0,16)

VLine("vwall2by2",6,0,3)
VLine("vwall2by2",RCMVD_Screen_Width-8,2,2)


HLine("hwall2by2",0,6,4)
HLine("hwall2by2",0,14,8)

VLine("vwall2by2",0,8,3)


HLine("hwall2by2",16,6,6)

HLine("hwall2by2",RCMVD_Screen_Width-8,6,4)
HLine("hwall2by2",RCMVD_Screen_Width-8,14,4)
VLine("vwall2by2",RCMVD_Screen_Width-8,16,2)
VLine("vwall2by2",6,16,2)

VLine("vwall2by2",14,6,4)
VLine("vwall2by2",26,8,4)

HLine("hwall2by2",6,20,16)

[/MapRender]

[/room]




[room]
number=1


[MapRender]


[/MapRender]

[/room]




kevin

#17
 RCMVD -  Shamus  - ROOMS and Map parsing & rendering  (2019-02-25)

     
       So another couple of hours and we have the [mapRender] blocks not only parsing, but rendering out the current room also.  

       The parsing code converts the function style calls in the textfile into typed structures within an opcode array.  I'm calling these opcodes as to render the scene we run through the opcodes and call the required functions top to bottom.   Now since the functions have different parameters, I've used inherited types to house each of the unique parameter patterns.    The init routines are stored in separate functions, so to call them we use CallFunction to dynamically pick the initialization routine.  

       The room loader stores to opcode array as dynamically allocated array handle within, so the room has a typed array associated within, but not strongly.  So if you deleted the room you'd be to manually delete the array handle.   Beyond that we have a render routine that stars from index zero of the opcodes array and loops through the types stored within it.    We use TypeOF() to ID what structure it is (in other words what OPCODE it is) in a select block, then set up a pointer to the parameters and call the function.   Sounds like a lot of works but it's pretty simple, just not the sort of thing most PlayBASIC coders do things.  

      Too late to make a progress video now, so here's the parsing code...  Hope you understand it 

PlayBASIC Code: [Select]
/* --------------------------------------------------------------------
--------------------------------------------------------------------
------------------>> MAP RENDER OPCODE STRUCTURES <<----------------
--------------------------------------------------------------------
--------------------------------------------------------------------
*/


Type tMapRenderOpCode_BASEOBJECT
status
EndType

Type tMapRenderOpCode_CLS as tMapRenderOpCode_BASEOBJECT
BlockName$
Colour$
EndType

Type tMapRenderOpCode_VLINE as tMapRenderOpCode_BASEOBJECT
BlockName$
X,Y,Size
EndType

Type tMapRenderOpCode_HLINE as tMapRenderOpCode_BASEOBJECT
BlockName$
X,Y,Size
EndType

Type tMapRenderOpCode_HLINECOLOUR as tMapRenderOpCode_BASEOBJECT
Colour$
EndType

Type tMapRenderOpCode_VLINECOLOUR as tMapRenderOpCode_BASEOBJECT
Colour$
EndType






Function LoadRoomsPack_PARSEMAPRENDER(Room as tRoom, StartIndex,EndIndex,buffer$())

OpcodeCount = endIndex - StartIndex
if opcodeCount>2
// Alloc an array of the base opcode type since we'll use
// inherited types form the parant to fill out the parameter
// structures
Room.MapRenderOpCodeArrayHandle = Private_AllocMapRenderOpCode_BASEOBJECT(OpCodeCount)

makeArray opcodes().tMapRenderOpCode_BASEOBJECT
opcodes() = Room.MapRenderOpCodeArrayHandle



Dim ParamBuffer$(255)

OpcodeIndex=0

for Opcode = StartIndex+1 to EndIndex -1

s$=Buffer$(Opcode)

if len(s$)
local Pos =instring(s$,"(")
if Pos
OpcodeName$=Left$(s$,pos-1)
Params$ =CutLeft$(S$,pos-1)
Params$ =trim$(Params$,"()")

ParamCount = Splittoarray(Params$,",",ParamBuffer$())

Select Upper$(OpcodeName$)
case "CLS"
opcodes(OpcodeIndex) = new tMapRenderOpCode_CLS
case "VLINE"
opcodes(OpcodeIndex) = new tMapRenderOpCode_VLINE
case "HLINE"
opcodes(OpcodeIndex) = new tMapRenderOpCode_HLINE
case "HLINECOLOUR"
opcodes(OpcodeIndex) = new tMapRenderOpCode_HLINECOLOUR
case "VLINECOLOUR"
opcodes(OpcodeIndex) = new tMapRenderOpCode_VLINECOLOUR

default
print "UNKNOWN OPCODE:"+Opcodename$
continue
EndSelect

// Dynamically Call the function to setup opcodes parametets
callfunction "PrivateMAPRENDERINIT_"+OpcodeName$, opcodes(OpcodeIndex), ParamCount, ParamBuffer$()

// move to the next opcode in the array
OpcodeIndex++
; print OpcodeName$
; print Params$

endif
endif
next
else


endif



; #break
EndFunction Handle



Function PrivateMAPRENDERINIT_CLS(Me as tMapRenderOpCode_CLS, ParamCount, Field$() )

if ParamCount=2
Me.BlockName$ = Field$(0)
Me.Colour$ = Field$(1)
else
#print "ERROR: MAPRENDER- CLS WRONG NUMBER OF PARAMS"
endif

EndFunction


Function PrivateMAPRENDERINIT_VLINE(Me as tMapRenderOpCode_VLINE, ParamCount, Field$() )

if ParamCount=4
Me.BlockName$ = Field$(0)
Me.x = val(Field$(1))
Me.y = val(Field$(2))
Me.size = val(Field$(3))
else
#print "ERROR: MAPRENDER- VLINE WRONG NUMBER OF PARAMS"
endif

EndFunction


Function PrivateMAPRENDERINIT_VLINECOLOUR(Me as tMapRenderOpCode_VLINECOLOUR, ParamCount, Field$() )

if ParamCount=1
Me.Colour$ = Field$(0)
else
#print "ERROR: MAPRENDER- VLINECOLOUR WRONG NUMBER OF PARAMS"
endif

EndFunction


Login required to view complete source code

kevin

#18
 
RCMVD -  Shamus   9 -  Room Loading - (2019-02-26)

     In this video we take a quick look at the room loading code as well as seeing it in action.    In the loader we use inherited types in help manage the MapRendering instructions.    This approach means the instructions for rendering the map can use custom parameters.  So at the end we end up with a  typed array (called opcode) that we use to draw the map to the screen.      The array is associated with the parant via a handle, so it means some care needs to taken with them but all in all you can have dynamic arrays nested within other types/arrays.  


Video:

   




Download:

   Attached bellow


kevin

#19
   RCMVD -  Shamus #10 -  Ray Intersecting Collision Map - (2019-02-27)


      For map collisions I thought we'd cheat a little and use some built in PlayBASIC back end to achieve what we need, which in this case, is ray intersections with the map, or more accurately the collision map.  So what i've done is added some collision properties to the 'blocks', so when the map is drawn it's drawing to the 8bit screen memory as well as drawing those hard chunks to a Map/Level that us used for collisions.  So there's a parallel set of data structures that are used for collisions   I which we'll also use the collision map to add some sliding of the bad guys.  

     In the attached shot we see a bunch of rays projected out from the mock up players position and impacting on the environment, if no intersection exists, it's drawn in green
otherwise the intersection point of the ray is used.  




Download:

    Attached bellow


kevin

#20
 RCMVD -  Shamus #11 -  Watch out for Variable Collisions - (2019-03-02)


      Well it's been one of those days where things are going well and the and then you hit a road block in this case a seemingly strange bug had  crept into the version 11 where the collision map would seemingly be deleted.  Seemed simple enough, probably had some typo or cut'n'paste error as normal :)  but then time rolled on and it still hasn't working..  So left to take the dog to dog park for a distraction, came home had something to eat watched a bit of crappy telly then fired up the laptop again fully expecting to find the problem quickly...  erm  NOPE


      Finally figured it out just before and it's one those interesting little gotchas..    Here's the condensed code to see if you to find it.. Should be easy :)


PlayBASIC Code: [Select]
   Type tPBCOLLISIONMAP
Map
EndType

Dim Collision as tPBCOLLISIONMAP
Private_Make_PlayBASIC_MAP()


print Collision.Map

RayIntersectRoomPolar(RoomIndex,x,y,angle#,radius#)


print Collision.Map



sync
waitkey





function Private_Make_PlayBASIC_MAP()
#print "Private_Make_PlayBASIC_MAP------------------->>>>>"

; Force the PB to use Map #1
Collision = new tPBCOLLISIONMAP

Collision.Map =1
CreateMap Collision.Map,255

oldsurface=getsurface()

// make the block image that is made up of
ThisImage=NewImage(256,8,2)
rendertoimage ThisImage
; cls 0
boxc 8,0,16,8,true,$ffffff
rendertoImage oldsurface

MakeMapGFX Collision.Map,ThisImage,8,8,16,rgb(0,0,0),2

EndFunction



Function RayIntersectRoomPolar(RoomIndex,x,y,angle#,radius#)
local ThisMAP =Collision.Map
local x2=x+cos(angle#)*Radius#
local y2=y+sin(angle#)*Radius#
Collision=RayHitMapPixels(ThisMAP,RoomIndex,x,y,x2,y2)
EndFunction Collision








stevmjon

at first i thought it was returning variable 'collision' from the function " RayIntersectRoomPolar() " without having anything to receive this value at the function call.
but it seems that you cannot use 'collision' as a variable because that name is used in the type variable 'collision.map'.
It's easy to start a program, but harder to finish it...

I think that means i am getting old and get side tracked too easy.

kevin

  Bingo,  the global typed variable "collision" and the return local variable "collision" are creating one of those chicken and egg situations.

  So in pass #1 of the compile the returned keyword is tagged as local, then in the pass#2 of the compile the expression Collision=RayHitMapPixels(ThisMAP,RoomIndex,x,y,x2,y2)   is assigning a new structure handle to the global typed variable.   So we end up with two key words in the same scope and the same name.   

  Could possibly throw a warning when the situation is detected but really the only way to pick it up would be doing variable assignments during pass #1 and not split between pass #1 and #2 in the compiler.

kevin

  Amiga Shamus Character Animations

Found this while picking through and old HD image from my Amiga days..  It's some animations i've made of the characters in the game Shamus.