(http://underwaredesign.com/PlayBasicSig.png)
Building Vic20 Shamus In PlayBASIC (Blog)
Started: 5th Jan 2019
ABOUT:
in this thread we're going to do something really crazy, we're going to re-create a VIC 20 styled display, then build game remake (tech demo) of the classic Vic 20 title
SHAMUS by
Tom Griner.
What is Shamus ?
The Vic20 was an 8bit compute by Commodore released in the early 1980's. It was one of the first colour home computers, notable for two things (that come to mind) it had a very low resolution display and a tiny amount od RAM, of 5K
Displaying Vic 20 Styled graphics in PlayBASIC
To achieve our goal we first have to write some code that will recreate something that resembles a Vic 20/C64 screen mode. The mode we're going for is the default 22 by 23 character map screen (from the VIC20). In this mode, the screen is not represented as an array of pixel data, but rather character indexes that range between 0 to 255 (8bit). What the system does to display the screen, is read the top left character index (think of it as a map), then uses that index as an offset into the character graphics in memory to draw that 8 by 8 pixel area to the screen, and so on for all the characters on the screen. To do this in PlayBASIC, I threw together a library called Retro Character Map Video Driver, which is a set of functions that decode our simulation memory and render it out as Pb image, the image is then scaled to the Pb screen to give us the real chunky pixel effect of the 8bit era.
Retro Character Map Video Driver (6th Jan 2019)
This is a video of the character map display mode as you might have seen on a Vic20 system back in the early 1980's. The screen is made up of 22 by 23 characters, each character is 8 by 8 pixels and has two colour controls in a separate colour map. The colour map lets you choose the colour of the background and foreground from a palette of 16 colours. So the character can only have two unique colours within it. This is not actually how the Vic20 / C64 worked, but similar enough for what i've got in mind.
The display routines are just big nest of fastPixel plots, where all the combinations are pre- computed in a On GOTO jump table. I didn't write the jump table, choosing to the write some code that would generate the source code for me.
Video: Music: https://BenSound.com
Download: Get Source Code (https://www.underwaredesign.com/forums/index.php?topic=4466.0)
Retro Character Map Video Driver - MULTI Sprite Test (2019-01-12) Here's the second look at our basic retro character map video driver library with sprite rendering logic added. Since the display is a character map we have to render our sprites into the character set dynamically. The drawing process comes down to or'ing the set bits (sprite pixels) with the character merging them together. The trick is dynamically reusing unused characters in our scene and merging unused characters into custom characters on the fly. We have a limit of 256 possible characters since the screen is a 8bit character map, so in our game we'll use say less than 50 characters for static maps.
The goal of this bizarre project is to create a Vic20 styled Shamus remake/tech demo in PlayBASIC. So what we want to get to do, is get this a point where we have some maps, collisions and basic game play up and running.
Video Download Attached
the year has come around fast. nearly 2 weeks into the new year already.
not a good way to start the year with an injury, stuck on the couch.
what happened to you? have you taught the dogs to fetch food from the fridge for you...
Quote from: stevmjon on January 12, 2019, 07:37:47 PM
not a good way to start the year with an injury, stuck on the couch.
what happened to you? have you taught the dogs to fetch food from the fridge for you...
Not entirely sure what I did it, was doing some mountain goat tricks cutting grass with the whipper sniper along our front fence line about a month ago. Pulled up lame in my left hip, then rather than rest, had some busy weeks running around and it just got worse. The doc thinks it's some pinched nerves, so imagine a few weeks of sharp ice pick styled pain shooting down your leg...
It's getting better, but it's like taking one step forward and two backwards at the moment.. Feel ok today.
I wish our dog Emma was able to do useful stuff, but unfortunately she passed away a couple of months ago.
Retro Character Map Video Driver - Loading Characters & Looking at Shamus for Mapping Ideas (2019-01-14) Here's the third step along our journey to the ridiculous, today we look at how we're importing Character definitions from a text file, which would have been completely impractical back in the day, but today it's pretty trivial, but ugly looking code. Since he was a way to get character definitions into memory we take a look at the original game to see about how we might represent the tile maps.
What you might not know is that Vic 20 only has 5K of ram.. Yes 5K... now this game came on cartridge, which was only 8 K of rom and still has over 30 rooms in it. :) - Now granted the rooms are sparse and even a poorly written run-length compression routine should make mince meat of them, it goes to show just how challenged the poor coder were back in the 1980's
For the maps, I think we'll store them as a series of line definitions. Most rooms would have less than 10 definitions, so it should be pretty easy to throw together something that will parse the rules for creating the level out of a text file. This way the user can tinker with the level data while it's running, which is always handy.
Video Download Attached
Retro Character Map Video Driver - Tile Animation Test 1 - (2019-01-17)
RCMVD - What Screen Mode Does Shamus Actually Use ?? (2019-01-19) Hmmm, so what screen mode does Shamus use on the Vic 20, that's todays rather stupid question ! Since the screen shots of Shamus seem show 16 * 16 tiles but the screen memory map seems to be smaller than our 22 by 23 characters... so erm, WTF... I asked some guys over on the Denial forums (http://sleepingelephant.com/denial/) who are up to date in Vic20 coding hardware, so we'll what they have to say..
Originally I just assumed the Vic only had 8 by 8 character modes, but I've seem an 8 by 16 pixel mode referenced. Not to sure how that actual works, so we'll wait and see.
RCMVD - Shamus Level Mock Up (2019-01-20 )
And finally here's a look at a recreation of the Shamus first level, the sprites aren't in as yet, so here's just working on the scale of the level, tiles and colouring. It's not quite right in this version but good enough to show you guys.
Video Download Attached
RCMVD - Shamus - Sprite Ripping - Sprite Pack Format and Loading (2019-01-23) Today we recap the changes made in the sixth example of the project, which focuses mainly on the process of grabbing/ripping sprite frames from a the video capture of Shamus running in the emulator, then converting those ripped sprites to a mark up (text) format.
Sprite-Pack Example
[Sprite]
name=Extra-Life-Sprite.png
size=16,16
[pixels]
0000111111000000
0000001100000000
0000001100000000
0000001100000000
0000111111000000
0011000000110000
1100110000001100
1100000011001100
1100001100001100
0011000000110000
0011111111110000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
[/pixels]
[/Sprite]
VideoDownload attached bellow.
RCMVD - Shamus - Ripped Sprite Test load and render (2019-01-25)
Finally got then sprites working after a somewhat stoooooppppppiiiiidd assumption in the loader code... That being the address in memory is where I think it is, of course it wasn't, it was way outside the bounds... making it do all sorts of odd things..
None the less, here's the test scene rendering random sprites from the pack instead of the alien sprite.
Download attached bellow.
Design Document Blues
Been working on adding design document / Idea pad to for the Shamus source code / tutorial / retro project. The document is just a list of defines about how characters and environment in the game / will work. Figured this might make a handy video before I jump in and start adding this stuff, but damn is editing a longer video a black hole of time .. We'll get there but the last few session have just been about editing.. Hopefully it'll be done tomorrow, even If i have to skimp a bit on the overlays.
#indiedev #gamedev #design #designdocument #howto #makegames
Retro Character Map Video Driver - Shamus - Sprite Collision Problems (2019-02-07) While working on a design document video (still not done yet) I needed to show some sprite collisions and thus begins the a couple of fun evenings of debugging the our sprite collision routine.
The routine does pixel perfect collisions between a sprite pair, it's just buggy atm. The routine first does a region check to see they overlap, if so, we move on to comparing the inner blocks of the sprite. If they overlap, we move down to comparing runs of pixels (bits), what's interesting is that no matter how many times you've done something in the past, there's always something that pops up to spoil the day.
Oh well, will try and get this working ASAP..
Video
Design Document Blues #2
Wow this video is taking forever.. Back working on this video today (when time permits).. pretty chessy but, it'll have to do.. The drama has been the length, cut about 10 minutes out and it's still 40 minutes long.. ouch... that nobody will watch :)
Getting there tho...
make the video as long as it needs to be. i would rather it was longer with the extra info, than cut shorter just to save video length.
if nobody watches it, i just hope at least nobody enjoyed it and leaves a like.
i hope your back is better now. pinched nerve doesn't sound fun.
sorry to read your dog emma passed away. i remember seeing a video you had with your dogs, when you got one from an animal shelter. which one was emma?
Hey Steve,
Quote from: stevmjon on February 15, 2019, 05:36:35 PM
make the video as long as it needs to be. i would rather it was longer with the extra info, than cut shorter just to save video length.
if nobody watches it, i just hope at least nobody enjoyed it and leaves a like.
The only stuff that got cut out was where i'd wander off into unrelated tangent :) Still has the usual amount of umm and ahhs etc.
Uploading now.. So it's good to get it done.
Quote
i hope your back is better now. pinched nerve doesn't sound fun.
It's a lot better thanks, more a niggle now than a screaming ice pick shooting down your leg.
Quote
sorry to read your dog emma passed away. i remember seeing a video you had with your dogs, when you got one from an animal shelter. which one was emma?
Emma was the stag hound (running around like an idiot) . We lost her about 5 months ago and have just adopted a Kelpie cross.
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
VideoDownload attached bellow.
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]
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
[pbcode]
/* --------------------------------------------------------------------
--------------------------------------------------------------------
------------------>> 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
Function PrivateMAPRENDERINIT_HLINE(Me as tMapRenderOpCode_HLINE, 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- HLINE WRONG NUMBER OF PARAMS"
endif
EndFunction
Function PrivateMAPRENDERINIT_HLINECOLOUR(Me as tMapRenderOpCode_VLINECOLOUR, ParamCount, Field$() )
if ParamCount=1
Me.Colour$ = Field$(0)
else
#print "ERROR: MAPRENDER- HLINECOLOUR WRONG NUMBER OF PARAMS"
endif
EndFunction
Function Rooms_Render_Map(RoomIndex)
;print "RenderMap"
;print RoomIndex
if RoomIndex>=0
if Rooms(RoomIndex)
dim Room as tRoom pointer
// Get pointer to this room data
Room=Rooms(RoomIndex).tRoom
// Alloc an array of the base opcode type since we'll use
// inherited types form the parant to fill out the parameter
// structures
if Room.MapRenderOpCodeArrayHandle
makeArray opcodes().tMapRenderOpCode_BASEOBJECT
opcodes() = Room.MapRenderOpCodeArrayHandle
; Param Structure pointers
Dim Cls_Param as tMapRenderOpCode_CLS pointer
Dim VLINE_Param as tMapRenderOpCode_VLINE pointer
Dim VLINECOLOUR_Param as tMapRenderOpCode_VLINECOLOUR pointer
Dim HLINE_Param as tMapRenderOpCode_HLINE pointer
Dim HLINECOLOUR_Param as tMapRenderOpCode_HLINECOLOUR pointer
HLINECOLOUR$ = "white"
VLINECOLOUR$ = "white"
for lp=0 to GetArrayElements(opcodes())
if opcodes(lp)
; Get Pointer to this structure
Ptr=opcodes(lp).tMapRenderOpCode_BASEOBJECT
; read this handle and get it's Type Index
Select typeof(opcodes(lp))
case tMapRenderOpCode_CLS
CLS_PAram = Ptr
; print "CLS"
BackColour = RCMVD_FindColourIndexByName(Cls_Param.Colour$)
RCMVD_Cls(0,BackColour,$1)
case tMapRenderOpCode_VLINE
VLINE_PAram = Ptr
Map_VLine(VLINE_Param.BlockName,VLINECOLOUR$,_
VLINE_Param.x,VLINE_Param.y,_
VLINE_Param.size)
case tMapRenderOpCode_VLINECOLOUR
VLINECOLOUR_PAram = Ptr
VLINECOLOUR$ = VLINECOLOUR_Param.Colour$
case tMapRenderOpCode_HLINE
HLINE_PAram = Ptr
Map_HLine(HLINE_Param.BlockName,HLINECOLOUR$,_
HLINE_Param.x,HLINE_Param.y,_
HLINE_Param.size)
;"hwall2by2",HCN$,6,0,16)
case tMapRenderOpCode_HLINECOLOUR
HLINECOLOUR_PAram = Ptr
HLINECOLOUR$ = HLINECOLOUR_Param.Colour$
default
print "UNKNOWN MAPRENDER OPCODE"
EndSelect
endif
next
endif
endif
endif
EndFunction
psub LOADROOMPACK_POSTERROR(Message$)
#print message$
endpsub
function Private_AllocMapRenderOpCode_BASEOBJECT(Size=255)
Dim MapRenderArray(Size) as tMapRenderOpCode_BASEOBJECT
EndFunction MapRenderArray().tMapRenderOpCode_BASEOBJECT
[/pbcode]
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
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
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 :)
[pbcode]
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
[/pbcode]
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'.
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.
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.