I'm currently re-making an old Missile Command-esque ZX Spectrum Next game I wrote a couple of years back that, to be honest, could have been way better (but I really didn't know any better back in those days). There are some graphical differences I want to make, and one of the major considerations regarding graphics is how I can get more speed when it comes to creating linear missile trails.
Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts
Saturday, 3 September 2022
Wednesday, 17 June 2020
What is MMUFPHK?
Posted on 14:09 by kevman3d
Yes, what is MMUFPHK? I recently ran into a situation regarding file management and team production which resulted in a task I decided to call Massive Multi User File Path House Keeping. This is where you (hopefully don't) find multiple Maya project folder structures that have become buried within a singular project on a team server, and worse - production files that are referencing or importing elements from a variety of these individual project folders.
Thursday, 21 May 2020
The NEXT Generation...
Posted on 02:27 by kevman3d
Saturday, 28 October 2017
Maya TD tips - that awesome 'toolkit' you always wanted...
Posted on 23:34 by kevman3d
I got tired - tired of seeing icons on shelves in Maya disappear and noting that even when working, flipping to shelves to change the tool set that was being used meant the pipeline tools that I'd painstakingly developed would be hidden in a shelf that probably wouldn't be set back again when needed.
Friday, 13 October 2017
Maya swatch headaches be-gone!
Posted on 01:24 by kevman3d
This was something that frustrated me this afternoon, so much that I thought I'd slap this very short and concise blog entry for anybody interested...
I loaded up a rather complex project to test that had been set up to use VRay materials, and had been choking up and causing havoc on the render-farm here.
I loaded up a rather complex project to test that had been set up to use VRay materials, and had been choking up and causing havoc on the render-farm here.
Sunday, 15 January 2017
Beeps and bleeps - playing Speccy music in python
Posted on 02:08 by kevman3d
While I was messing about with converting old UDG graphics to PNG files, I figured one of the other things I was curious to recreate was the beepy music from the ZX Spectrum. BASIC code loaded with BEEP commands created that "I wonder what does that sound like?" curiosity that made me wonder just how easily I could use python to listen to the classic Sinclair hits of the 80's...
One problem I did run into was getting the beepy-sound out nicely. I tried many of the tried-n-tested code examples online that generated a sine wave - however while it worked, the final audio sounded pretty odd and didn't have that nice clean bleep I was expecting. I figured I'd come back to this later...
Exporting aside, I felt there must be a better way to just listen to the music - afterall, its that curiosity of hearing what it sounded like I was after. Lucky python has a module designed just for the task... The winsound module. I also wanted to create pauses in the music, so I imported the sleep function from the time module as well.
Yup, winsound has a Beep function (note the uppercase B). Much like the ZX Spectrum's own beep, you just pass the note and duration. Sounds like it should be a real doddle!
Frequency = base-note * a^semitone
Where base-note is the lowest frequency of your musical scale (in this case, I decided to go for 3 octaves below which is 32.70 hertz). The value of a is calculated as the 12th root of 2 ( in nerdy math terms, its 2^(1/12) ).
In case you're wondering about the code below, 0.083 is 1 / 12.
As there were often pauses added through music, I needed a way to indicate this. I used a note value of 99 to signal a pause.
Looping through this list, I read the duration and note value. The duration is slightly different between the winsound.Beep function, and the sleep() function that I used to introduce pauses. The Beep function requires the length in milliseconds. This is simply the duration from the list multiplied by 1000. The sleep function just uses the value (number of seconds) directly.
The rest of the code was a piece of cake. I feel there's no real explanation necessary as the code can speak for itself...
To export or just listen?
I tried two methods. One was to make use of python's wave module. This is a module that allows you to work with audio files. That includes the ability to both read and write. Being able to export the music to a .wav mean't that it could be bought into those recreated games and used.One problem I did run into was getting the beepy-sound out nicely. I tried many of the tried-n-tested code examples online that generated a sine wave - however while it worked, the final audio sounded pretty odd and didn't have that nice clean bleep I was expecting. I figured I'd come back to this later...
Exporting aside, I felt there must be a better way to just listen to the music - afterall, its that curiosity of hearing what it sounded like I was after. Lucky python has a module designed just for the task... The winsound module. I also wanted to create pauses in the music, so I imported the sleep function from the time module as well.
import winsound
from time import sleep
from time import sleep
Yup, winsound has a Beep function (note the uppercase B). Much like the ZX Spectrum's own beep, you just pass the note and duration. Sounds like it should be a real doddle!
Hmmm, note vs. frequency
ZX Spectrum audio uses semitone numbers. A value of 0 is middle C. 1 is the next semitone of C# / Db, 2 is D and so on. However winsound.Beep required a frequency value (in hertz). How do I translate that number into a frequency?Did I mention I suck at maths?
Maths was never my strong suit at school, but luckily for me that's where the internet comes in with the answers! The formula for calculating a frequency is simplyFrequency = base-note * a^semitone
Where base-note is the lowest frequency of your musical scale (in this case, I decided to go for 3 octaves below which is 32.70 hertz). The value of a is calculated as the 12th root of 2 ( in nerdy math terms, its 2^(1/12) ).
Maths always looks easier in code
I created a function to calculate the correct frequency from the beep value. I calculated this from the lowest frequency of 32.70... As I knew middle C (beep value of 0) was three octaves higher, I just added 36 (which was 3 * 12 semitones) to the value first...In case you're wondering about the code below, 0.083 is 1 / 12.
def beepFreq(ZXVal):
zxNote = ZXVal + 36
a = 2.0 ** 0.083
freq = 32.70 * (a**zxNote)
return freq
zxNote = ZXVal + 36
a = 2.0 ** 0.083
freq = 32.70 * (a**zxNote)
return freq
Getting them tunes down...
The music data itself I passed as a sequence of tuples in a list, copied directly from the BEEP parameters in the spectrum listing. This isn't the most musical of pieces, but it came from a listing so it was a good test...As there were often pauses added through music, I needed a way to indicate this. I used a note value of 99 to signal a pause.
musicData = [ (.1,0),(.1,0),(.1,2),(.1,2),(1,0),(1,99),
(.1,0),(.1,4),(.1,4),(.1,0),(.1,0),(.1,2),(.1,2),
(.1,-1),(.1,-1),(.1,0),(.1,0)]
(.1,0),(.1,4),(.1,4),(.1,0),(.1,0),(.1,2),(.1,2),
(.1,-1),(.1,-1),(.1,0),(.1,0)]
Looping through this list, I read the duration and note value. The duration is slightly different between the winsound.Beep function, and the sleep() function that I used to introduce pauses. The Beep function requires the length in milliseconds. This is simply the duration from the list multiplied by 1000. The sleep function just uses the value (number of seconds) directly.
The rest of the code was a piece of cake. I feel there's no real explanation necessary as the code can speak for itself...
for musicPlay in musicData:
# Calculate the duration (in milliseconds)
duration = int(musicPlay[0] * 1000)
# Work out if we play a note, or whether this is a pause
if musicPlay[1] == 99:
sleep(musicPlay[0])
else:
note = int(beepFreq(musicPlay[1]))
# Call the Winsound Beep
winsound.Beep(note,duration)
# Calculate the duration (in milliseconds)
duration = int(musicPlay[0] * 1000)
# Work out if we play a note, or whether this is a pause
if musicPlay[1] == 99:
sleep(musicPlay[0])
else:
note = int(beepFreq(musicPlay[1]))
# Call the Winsound Beep
winsound.Beep(note,duration)
Budum-tish!
And there you have it. Go grab those old ZX Spectrum basic listings and type in the beep values to enjoy all of those bleepy tunes that were part and parcel of games in the 80'sSaturday, 14 January 2017
Retro bytes to PNG pixels with Python
Posted on 21:08 by kevman3d
After a long break from doing any retro game recreation in Python, I decided that since I had a few old personal game listings laying around from my ZX Speccy days that the idea of converting these to python could be fun at some later date.
However one thing the ZX Spectrum had that we didn't see on the ZX81 (with its lack of any real graphics capability) was user defined graphics, or UDG's as they were termed.
However one thing the ZX Spectrum had that we didn't see on the ZX81 (with its lack of any real graphics capability) was user defined graphics, or UDG's as they were termed.
Monday, 7 November 2016
Maya 2016 - small changes make for some small headaches
Posted on 21:52 by kevman3d
Lately I've been running into a few issues with projects and scripts that relate to some small changes made in Maya 2016, and 2016.5. Like most of my discoveries, I'm posting them to the blog here for those of you who may find them of use...
Saturday, 29 October 2016
Subfolders, referencing - Yes, its more Maya scripting fun!
Posted on 01:27 by kevman3d
In the last couple of weeks, I've had to modify and tweak all the scripts that I developed for my students to automate file management from Maya. In the process, I added new features and added extra functionality to handle some of the changes that this class had decided to implement.
One of my students asked me if I would be sharing these things on my blog - so yup, here we go - again... I have a few, but to keep these posts nice and short I'll start with just a couple.
One of my students asked me if I would be sharing these things on my blog - so yup, here we go - again... I have a few, but to keep these posts nice and short I'll start with just a couple.
Wednesday, 29 June 2016
Yup, its time for more Maya python tips...
Posted on 01:12 by kevman3d
Well, I couldn't really keep away from posting Maya python material forever, so here's another installment of various tips, techniques and well, python-goodness! This time I'm throwing up a handful of additional snippets of code for your tool productions and UI's...
Monday, 8 February 2016
Learning technical stuff from books can be fun...
Posted on 20:33 by kevman3d
Usborne books were a great childhood favorite when it came to pretty much anything. They were filled with cool drawings and the way they visually showed how things worked was a great way to teach all kinds of fascinating stuff for kids (and adults for that matter).
When it came to computing, they were a great resource to coming to grips with all that new technology!
When it came to computing, they were a great resource to coming to grips with all that new technology!
Sunday, 7 February 2016
Let Python be with you... Always...
Posted on 02:54 by kevman3d
Sunday, 17 January 2016
Automating Maya render layers with Python
Posted on 17:57 by kevman3d
I had hoped to take a break from writing up Maya python related articles on the blog, but hey - when there's plenty to share, may as well keep popping it up. This time, its all about building a tool (rather then a UI) for automating the render layer setup process...
Sunday, 10 January 2016
Python (Maya UI) - just finishing up the window
Posted on 02:31 by kevman3d
Ok - I was tossing up whether to post a final few very basic tips of UI advice here, just to finish up for now on working with UI's in general. But hey, I think just for new users, it doesn't hurt to add those little tweaks and answer some very simple questions I'd been asked in the past....
Saturday, 12 September 2015
Python and BASIC games, mumble, mumble, mumble...
Posted on 16:39 by kevman3d
A few weeks back, I presented my project here to the New Zealand Auckland Python Users group meetup.
They web stream everything via Google hangouts, and as expected often the quality can suffer a little, however if anybody's interested in an hour of me mumbling my way through it, you can view it online here...
They web stream everything via Google hangouts, and as expected often the quality can suffer a little, however if anybody's interested in an hour of me mumbling my way through it, you can view it online here...
Sunday, 5 July 2015
BASIC games in Python - 1982 would be proud (Part 3)
Posted on 14:29 by kevman3d
Ok, this is it - the one and only part 3, the final chapter. We looked at pygame in part 1, we deciphered the BASIC code in part 2,and now we're gonna produce a python version of this simple little 1K ZX81 game.
Is it purely to learn about converting old 1980's programs to python? Well, in a way yes - its something I've wanted to do given how much python reminded me of my youth. I am enthralled with the fact I can code games and have fun just as much as I did back at age 11.
But one thing I am hoping to instill on everybody who reads this article is that writing games is a great way to learn to program. Programming is a a technical art form that has mostly disappeared these days. Lets face it, nobody really goes and buys a high specced PC or Mac to sit and write programs - its mainly about playing the latest games, running business applications, browsing the web and making media. And that's not a bad thing, but its also a sad thing.
If you're just getting into programming and need a challenge to help you learn, consider looking at the past (I've provided links at the bottom of this page). This article is about using that 80's approach of learn through example. By reverse-engineering a game from another language, you really can learn a LOT. Concepts that were used back in that era to make the most of the hardware (ie. tricks like using boolean logic for scores), the way game logic works and of course you pick up a lot about the programming language you're using by trying to work out how to create code that works the same way.
Don't forget - its a case of practise makes perfect. In the 80's, typing in games and understanding how they worked was how I learned to create my own code... Today, the same concept is just as relevant - the more you do, the more you learn - and the more your expertise grows.
Its these reasons I've written this 3-part project for. To try and bring a little of the past to the current day, and just show how much fun it can be to code games rather then click-drag them.
That said, programming is a skill that's started being taught again in schools - and its great to see young people getting this opportunity. In fact, I'm off to a secondary school in a couple of weeks to teach kids some python! Very exciting.
So, lets get on with it.
When files sit in the same folder as your python project, we can load them easily by just specifying the filename - so put those fonts there. There are two .ttf files - zx81.ttf and zxspectr.ttf... Great font, and does a great job too.
If you don't want to use a custom font, for a project like this you'll need to consider using a monospaced font - one where all the characters (including space) are identical widths. Courier for instance works just fine.
However, as great as the font is, we still need those clunky graphic characters. The best way to handle this is to simply these draw up yourself. 8 pixels high by 16 pixels wide... The thickness of the black blocks are 4 pixels.
OK - enough natter, lets start to code this game and see how we fare. I'll be showing code here in this article without my usual python comments in it - but don't fear as I'll be explaining what each part does below the code itself.
In this block of code, its worth noting that I've loaded all (indicated by the *) of pygame.locals into the root namespace. What's meant by this is that rather then referring to a command using its module namespace we can now refer to it directly. I did this because it saves me having to write pygame.locals before each constant. Now I can just refer to pygame.locals.QUIT simply as QUIT.
You don't need to do this, mind you. In the end you can just load them as you would otherwise if you prefer, but its worth considering.
Of course, we also need to bring in randint - a function that lets us ask for a random integer (used to determine where the coin would fall). This I'm also loading into the root namespace from the random module.
So that our code will be easier to understand, and to save us repeating ourselves (plus in case we wanted to change a value used throughout a program) we will define a few constants here that we can use everywhere.
BLACK and WHITE are defining the RGB values that we can use for drawing text, wiping the screen, etc. The TXT constant is for text scaling - for instance, using the original pixel size of the ZX81 on a PC with a 1920x1080 resolution will be tiny and hard to look at. This value will be used to let us change the size of all of our character elements. In this case, I've used a size of 16 pixels per character.
Obviously before we can use pygame, we need to set it up (see part 1 for an explanation about how this works). This includes not just the pygame system, but loading in fonts and graphics that we will be using.
The ZX81 screen is 32 characters wide, 22 characters high (each character taking up 8 x 8 pixels). We therefore need to set up our window appropriately, using our TXT scale constant.
We want to import our zx81.ttf font. Providing you've placed it into the same folder, we can refer to it directly by file name. If you've instead installed the font into your system - or you want to use an alternate installed font - then you'll need to request the path to the font file itself. That's fairly easy to do by asking for it using pygame.font.match_font('nameoffont').
For now, lets just load up the ZX81 font, and set its pixel size to TXT
We can import our nifty 8 x 16 pixel graphic and then resize it to match the rest of the game as follows. In this line of code, I'm using pygame's transform.scale command to resize the file that I'm loading from disk... You could of course break this into two lines - load image, and then scale - if you prefer.
As the width of the players graphic is double the height, we set the scale values to ( 2 * TXT, TXT)
Note : If you are unfamiliar with python, long code that is encapsulated between brackets (such as our parameters for the pygame.transform.scale command) can be broken apart into multiple lines. This is termed implicit line continuation (python regards anything inside brackets, strings or parentheses as continuous until the brackets/etc are closed. This includes code that is broken up over multiple lines)
We'll now define some functions... For our first one, lets define a function that takes care of simulating BASIC's PRINT AT command. This will make life a lot easier for us. If we can pass the same settings as the BASIC code, we'll end up making this code a real doddle to complete.
In the case of pygame, text and graphics are treated the same way. Text, as we'll see in our game code, is rendered to an image first before its drawn (blitted) to the gameWindow.
The game itself - that part of the BASIC listing that does 10 rounds of coin catching - is being defined as a function. Why, you may ask, would we want our game in a function?
Its simple... Our game loop (which is the infinite loop that we write at the end of the program after the function) needs to be able to restart the game. By making the game a function, we can call it from our main game loop. When the game is over, if we then have the function return back to our main game loop (passing the score back as well), we can print the score and wait for a key press to restart the game (ie. call the game function).
We start by defining our score, and our players position in the game. In Python we can do it in one line, rather then then two like BASIC.
As in the original version, we have a for loop that gives us our 10 rounds of coin catching. the gameWindow.fill does the equivalent of the CLS command by filling the display with white.
After that, the score is rendered to an image that is stored in the variable bText, then we draw it to our display using our handy bPRINT function
Next we choose our random value we'll use to position our coin at. And then we're ready to catch that coin with our Y loop...
Obviously we add in our Y loop. The coin drops 10 characters downwards from the top of the screen. Amusingly this does make it impossible to catch coins that fall over 10 characters away from the player... But hey, we'll keep to the original here and retain that particular retro-feature.
Before we jump into the game code, the first thing I've added is a test to see if the user has quit the game. Quitting the game is called when the user closes the window... We want to make sure we clean up nicely in that case. Obviously this isn't something that we needed in BASIC - you'd never quit a program since it was the only thing running on the computer of course...
You'd notice that by importing the pygame.locals into the root namespace, I only needed to type the constant name QUIT here. It looks cleaner, and its easier to type.
For the key press - the code is literally the same. The only difference is that INKEY$ is replaced by pygame's pygame.key.getpressed() function. As expected, both of these also return a True (1) or False (0) result.
Note : much like the transform.scale code, we've broken up this line with a line continuation character (back slash). If your code isn't continuously encapsulated between brackets, parentheses or a string, we need this character to indicate the line continues.
Again, almost exactly the same as the original code - we check for our bounds, and then we print the coin (the letter "O") to the screen. This is just too easy! (which is a good thing)
We draw our player graphic...
Now that we've drawn our graphics, we simply need to update it to show it on screen.
Finally we delete our graphics (the coin, and the player) - we can do this by simply printing a space character (or in the case of the players graphic, two spaces). We can only do this, of course, as long as we are using a monospace font... Any other type of font tends to have thinner spaces, which will only partially blank out half (or less) of the graphic.
We update our position to use the new position we calculated, and we set the frames per second to delay the game to keep it running at the right speed. On my PC, I found a frame rate of 6 fps felt the most accurate to embrace the processing power of the ZX81
And that's the end of the Y loop.
Once the coin has dropped all the way down (10 characters), the loop will end and continue into the next line... This is where we'll update our score (again using the same logic from the original program) before the Z loop repeats.
Once the Z loop is complete, the function exits. As we'll want to print the final score, this function simply needs to return T...
We call the playDropout() function which will run the game, and then exit on completion of 10 rounds of coin dropping.
We returned the score back, which will be stored in fSc. Lets print that back to the player...
We won't see this until we refresh the display.
Finally, we want to pause until we press a key - well, in this case I'm going to set it to be the space bar, simply because its possible we may still be holding down a key from playing the game - which of course will jump us straight back into the game. We also want to make sure we check for the user quitting the application.
This can all be done with a while loop that waits for the space bar to be pressed.
And that's it! Once the space bar is pressed, the code will jump back into our while loop, running the playDropout() function once more. Game on!
If your catching the letter O and experiencing the excitement of doing that 10 times then you've successfully managed to write your own recreation of an old BASIC game in python! In fact, what the aim of doing this was to demonstrate that you can code games very easily with python, as easily as kids did 33 years ago using BASIC.
Of course, BASIC was a language found on most home computers. While the dialect may be different, the logic behind how the language worked is no different - and there were just as many amazing games on other platforms as there were on just the ZX81. Challenge yourself to find some of the amazing games that were around and learn to convert them into python. You'll be amazed how much you will discover from looking at how people developed their code.
Books, including some that explain how to program in a particular flavor of BASIC can also be found online. Some other archives and sites worth looking into include Atariarchives, Folkscanomy, and the original publisher of well-loved books - Usborne (who have released a lot of their old 80's computer programming books for free - I wrote an article here). You can actually read the ZX81 programming guide online if you want to understand the language more.
...and surprisingly the excellent collection of cassette software at ZX81stuff also display basic listings on screen when you click on them. Back in the day, a lot of early 'commercial' games software was in fact written in BASIC.
Why, oh why am I doing this?!
If you've sat through the previous 2 parts, then that's a question you're probably asking...Is it purely to learn about converting old 1980's programs to python? Well, in a way yes - its something I've wanted to do given how much python reminded me of my youth. I am enthralled with the fact I can code games and have fun just as much as I did back at age 11.
But one thing I am hoping to instill on everybody who reads this article is that writing games is a great way to learn to program. Programming is a a technical art form that has mostly disappeared these days. Lets face it, nobody really goes and buys a high specced PC or Mac to sit and write programs - its mainly about playing the latest games, running business applications, browsing the web and making media. And that's not a bad thing, but its also a sad thing.
If you're just getting into programming and need a challenge to help you learn, consider looking at the past (I've provided links at the bottom of this page). This article is about using that 80's approach of learn through example. By reverse-engineering a game from another language, you really can learn a LOT. Concepts that were used back in that era to make the most of the hardware (ie. tricks like using boolean logic for scores), the way game logic works and of course you pick up a lot about the programming language you're using by trying to work out how to create code that works the same way.
Don't forget - its a case of practise makes perfect. In the 80's, typing in games and understanding how they worked was how I learned to create my own code... Today, the same concept is just as relevant - the more you do, the more you learn - and the more your expertise grows.
Its these reasons I've written this 3-part project for. To try and bring a little of the past to the current day, and just show how much fun it can be to code games rather then click-drag them.
That said, programming is a skill that's started being taught again in schools - and its great to see young people getting this opportunity. In fact, I'm off to a secondary school in a couple of weeks to teach kids some python! Very exciting.
So, lets get on with it.
Step 1 - make sure we have those things we can't program...
While the game can be coded, to really achieve that classic look we need to have the classic font and graphics! There are some great free ZX-style fonts that people have created - I grabbed this one, and unzipped the files into the folder where I was writing my python script.| These are not the droids you are looking for, but definitely that font is... |
When files sit in the same folder as your python project, we can load them easily by just specifying the filename - so put those fonts there. There are two .ttf files - zx81.ttf and zxspectr.ttf... Great font, and does a great job too.
If you don't want to use a custom font, for a project like this you'll need to consider using a monospaced font - one where all the characters (including space) are identical widths. Courier for instance works just fine.
However, as great as the font is, we still need those clunky graphic characters. The best way to handle this is to simply these draw up yourself. 8 pixels high by 16 pixels wide... The thickness of the black blocks are 4 pixels.
Get the files here...
You can download the extremely tiny graphic (along with the source code for this game) here... Obviously for permission reasons, I can't re-distribute the font - but here's that link to it again for convenience.OK - enough natter, lets start to code this game and see how we fare. I'll be showing code here in this article without my usual python comments in it - but don't fear as I'll be explaining what each part does below the code itself.
Modules
To start off, we'll need to import our modules of course.
import pygame
import pygame.time
import sys
from pygame.locals import *
from random import randint
import pygame.time
import sys
from pygame.locals import *
from random import randint
In this block of code, its worth noting that I've loaded all (indicated by the *) of pygame.locals into the root namespace. What's meant by this is that rather then referring to a command using its module namespace we can now refer to it directly. I did this because it saves me having to write pygame.locals before each constant. Now I can just refer to pygame.locals.QUIT simply as QUIT.
You don't need to do this, mind you. In the end you can just load them as you would otherwise if you prefer, but its worth considering.
Of course, we also need to bring in randint - a function that lets us ask for a random integer (used to determine where the coin would fall). This I'm also loading into the root namespace from the random module.
Constants
So that our code will be easier to understand, and to save us repeating ourselves (plus in case we wanted to change a value used throughout a program) we will define a few constants here that we can use everywhere.
BLACK = (0,0,0)
WHITE = (255,255,255)
TXT = 16
WHITE = (255,255,255)
TXT = 16
BLACK and WHITE are defining the RGB values that we can use for drawing text, wiping the screen, etc. The TXT constant is for text scaling - for instance, using the original pixel size of the ZX81 on a PC with a 1920x1080 resolution will be tiny and hard to look at. This value will be used to let us change the size of all of our character elements. In this case, I've used a size of 16 pixels per character.
Getting pygame set up
pygame.init()
fpsClock = pygame.time.Clock()
fpsClock = pygame.time.Clock()
Obviously before we can use pygame, we need to set it up (see part 1 for an explanation about how this works). This includes not just the pygame system, but loading in fonts and graphics that we will be using.
gameWindow = pygame.display.set_mode(( 32 * TXT, 22 * TXT ))
The ZX81 screen is 32 characters wide, 22 characters high (each character taking up 8 x 8 pixels). We therefore need to set up our window appropriately, using our TXT scale constant.
Fonts
We want to import our zx81.ttf font. Providing you've placed it into the same folder, we can refer to it directly by file name. If you've instead installed the font into your system - or you want to use an alternate installed font - then you'll need to request the path to the font file itself. That's fairly easy to do by asking for it using pygame.font.match_font('nameoffont').
bFont = pygame.font.Font('zx81.ttf', TXT)
For now, lets just load up the ZX81 font, and set its pixel size to TXT
Player graphic
pGfx = pygame.transform.scale(pygame.image.load('ZX81_block.png'),
(2*TXT,TXT))
(2*TXT,TXT))
We can import our nifty 8 x 16 pixel graphic and then resize it to match the rest of the game as follows. In this line of code, I'm using pygame's transform.scale command to resize the file that I'm loading from disk... You could of course break this into two lines - load image, and then scale - if you prefer.
As the width of the players graphic is double the height, we set the scale values to ( 2 * TXT, TXT)
Note : If you are unfamiliar with python, long code that is encapsulated between brackets (such as our parameters for the pygame.transform.scale command) can be broken apart into multiple lines. This is termed implicit line continuation (python regards anything inside brackets, strings or parentheses as continuous until the brackets/etc are closed. This includes code that is broken up over multiple lines)
Functions - its where the fun begins...
def bPRINT ( cRow, cCol, cGFX ):
gameWindow.blit(cGFX, (cCol * TXT, cRow * TXT))
gameWindow.blit(cGFX, (cCol * TXT, cRow * TXT))
We'll now define some functions... For our first one, lets define a function that takes care of simulating BASIC's PRINT AT command. This will make life a lot easier for us. If we can pass the same settings as the BASIC code, we'll end up making this code a real doddle to complete.
In the case of pygame, text and graphics are treated the same way. Text, as we'll see in our game code, is rendered to an image first before its drawn (blitted) to the gameWindow.
The Game - a function?
The game itself - that part of the BASIC listing that does 10 rounds of coin catching - is being defined as a function. Why, you may ask, would we want our game in a function?
Its simple... Our game loop (which is the infinite loop that we write at the end of the program after the function) needs to be able to restart the game. By making the game a function, we can call it from our main game loop. When the game is over, if we then have the function return back to our main game loop (passing the score back as well), we can print the score and wait for a key press to restart the game (ie. call the game function).
playDropout - where BASIC and Python meet...
Get ready because this is where we translate that old BASIC code into its python equivalent. We'll stick to using the same variable names as well as the FOR loops as the original code so that we can see the similarities...
def playDropout():
T,P = 0,0
T,P = 0,0
We start by defining our score, and our players position in the game. In Python we can do it in one line, rather then then two like BASIC.
for Z in range(10):
gameWindow.fill(WHITE)
bText = bFont.renders("%d" % T, True, BLACK, WHITE)
bPRINT(12,0,bText)
gameWindow.fill(WHITE)
bText = bFont.renders("%d" % T, True, BLACK, WHITE)
bPRINT(12,0,bText)
As in the original version, we have a for loop that gives us our 10 rounds of coin catching. the gameWindow.fill does the equivalent of the CLS command by filling the display with white.
After that, the score is rendered to an image that is stored in the variable bText, then we draw it to our display using our handy bPRINT function
R = randint(0,16)
Next we choose our random value we'll use to position our coin at. And then we're ready to catch that coin with our Y loop...
The coin falling...
for Y in range(10):
Obviously we add in our Y loop. The coin drops 10 characters downwards from the top of the screen. Amusingly this does make it impossible to catch coins that fall over 10 characters away from the player... But hey, we'll keep to the original here and retain that particular retro-feature.
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == QUIT:
pygame.quit()
sys.exit()
Before we jump into the game code, the first thing I've added is a test to see if the user has quit the game. Quitting the game is called when the user closes the window... We want to make sure we clean up nicely in that case. Obviously this isn't something that we needed in BASIC - you'd never quit a program since it was the only thing running on the computer of course...
You'd notice that by importing the pygame.locals into the root namespace, I only needed to type the constant name QUIT here. It looks cleaner, and its easier to type.
N = P+(pygame.key.getpressed()[K_4])- \
(pygame.key.getpressed()[K_1])
(pygame.key.getpressed()[K_1])
For the key press - the code is literally the same. The only difference is that INKEY$ is replaced by pygame's pygame.key.getpressed() function. As expected, both of these also return a True (1) or False (0) result.
Note : much like the transform.scale code, we've broken up this line with a line continuation character (back slash). If your code isn't continuously encapsulated between brackets, parentheses or a string, we need this character to indicate the line continues.
if N < 0 or N > 15:
N = P
bText = bFont.render("O", True, BLACK, WHITE)
bPRINT(Y,R,bText)
N = P
bText = bFont.render("O", True, BLACK, WHITE)
bPRINT(Y,R,bText)
Again, almost exactly the same as the original code - we check for our bounds, and then we print the coin (the letter "O") to the screen. This is just too easy! (which is a good thing)
bPRINT(11,N,pGfx)
We draw our player graphic...
pygame.display.update()
Now that we've drawn our graphics, we simply need to update it to show it on screen.
bText = bFont.render(" ", True, BLACK, WHITE)
bPRINT(Y,R,bText)
bText = bFont.render(" ", True, BLACK, WHITE)
bPRINT(11,N,bText)
bPRINT(Y,R,bText)
bText = bFont.render(" ", True, BLACK, WHITE)
bPRINT(11,N,bText)
Finally we delete our graphics (the coin, and the player) - we can do this by simply printing a space character (or in the case of the players graphic, two spaces). We can only do this, of course, as long as we are using a monospace font... Any other type of font tends to have thinner spaces, which will only partially blank out half (or less) of the graphic.
P = N
fpsClock.tick(6)
fpsClock.tick(6)
We update our position to use the new position we calculated, and we set the frames per second to delay the game to keep it running at the right speed. On my PC, I found a frame rate of 6 fps felt the most accurate to embrace the processing power of the ZX81
And that's the end of the Y loop.
T = T + (P == R or P+1 == R)
Once the coin has dropped all the way down (10 characters), the loop will end and continue into the next line... This is where we'll update our score (again using the same logic from the original program) before the Z loop repeats.
return T
Once the Z loop is complete, the function exits. As we'll want to print the final score, this function simply needs to return T...
On to the main event, eh, loop
This is the end - our game is written as a function, all our initialisation is finished so we simply need to now put it all together in our game loop...
while True:
fSc = playDropout()
fSc = playDropout()
We call the playDropout() function which will run the game, and then exit on completion of 10 rounds of coin dropping.
bText = bFont.render("YOU SCORED %d/10" % fSc, True,BLACK, WHITE)
bPRINT(12,0,bText)
bPRINT(12,0,bText)
We returned the score back, which will be stored in fSc. Lets print that back to the player...
pygame.display.update()
We won't see this until we refresh the display.
replay = False
while not replay:
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_SPACE:
replay = True
if event.type == QUIT:
pygame.quit()
sys.exit()
while not replay:
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_SPACE:
replay = True
if event.type == QUIT:
pygame.quit()
sys.exit()
Finally, we want to pause until we press a key - well, in this case I'm going to set it to be the space bar, simply because its possible we may still be holding down a key from playing the game - which of course will jump us straight back into the game. We also want to make sure we check for the user quitting the application.
This can all be done with a while loop that waits for the space bar to be pressed.
And that's it! Once the space bar is pressed, the code will jump back into our while loop, running the playDropout() function once more. Game on!
Final Comparison
If we remove all that additional Python code for rendering text, event catching, etc and look at the core code that actually is the game, we can see the way that the two languages compare - and its really not that different!Congratulations!
If your catching the letter O and experiencing the excitement of doing that 10 times then you've successfully managed to write your own recreation of an old BASIC game in python! In fact, what the aim of doing this was to demonstrate that you can code games very easily with python, as easily as kids did 33 years ago using BASIC.
Keep going!
If you don't write games at all - or want to learn more about how to develop your skills as both a programmer and a game developer then don't dismiss the opportunity presented before you. Learn through typing in others games.Of course, BASIC was a language found on most home computers. While the dialect may be different, the logic behind how the language worked is no different - and there were just as many amazing games on other platforms as there were on just the ZX81. Challenge yourself to find some of the amazing games that were around and learn to convert them into python. You'll be amazed how much you will discover from looking at how people developed their code.
Resources online
There are lots of BASIC listings to be found online thanks the those people who are passionately preserving the past. Magazine's are preserved and available from web archives such as retropdfs, the computer magazines archive, Atarimagazines, DPLibrary, etc.Books, including some that explain how to program in a particular flavor of BASIC can also be found online. Some other archives and sites worth looking into include Atariarchives, Folkscanomy, and the original publisher of well-loved books - Usborne (who have released a lot of their old 80's computer programming books for free - I wrote an article here). You can actually read the ZX81 programming guide online if you want to understand the language more.
...and surprisingly the excellent collection of cassette software at ZX81stuff also display basic listings on screen when you click on them. Back in the day, a lot of early 'commercial' games software was in fact written in BASIC.
So...
I'm definitely happy to be able to see how easy it is to code games the same way I did 33 years ago... And no - I've no real plans to continue just re-writing old BASIC code in python. Roll on new, modern games!Saturday, 4 July 2015
BASIC games in Python - 1982 returns (Part 2)
Posted on 19:43 by kevman3d
Welcome to part 2...
We're going to break down an old 1k ZX81 game written in BASIC in 1982. This code is a scan I made from the first-ever book I bought... "34 Amazing Games for the 1K ZX81" by Alastair Gourlay. It taught me a load of techniques that would have taken a long time to pick up on my own, such as reading keys and drawing characters moving on screen.
In the books foreword, written by Tim Hartnell (a name well known in the 80's when it came to programming books), it's pretty clear that the aim of the book is more then just a collection of games to play...
You'll probably want to enter the programs just as they are the first time you read through the book. We'd like you to adapt them, improve and modify them to put your own stamp on them. And we're sure the programming tricks Alastair has used to compress the programs into 1K can be used by you in other programs.
This book is much more then just a collection of games. It is a guidebook to show you how to make the most of your 1K ZX81
Its simple, but when you compare the game play to something like the runner game concept that is all over the mobile market where the aim is to just collect as many items while avoiding the obstacles, the concept of simplicity is really no different. The appeal comes in the whole 'one more go to get a better score' of what is a very basic game play model.
Breaking down the code into blocks, there are 4 things that are happening in here.
(A) We initialise the game by resetting the score, and setting the player starting position.
(B) This is followed by the game in not one, but two loops... The blue loop (game loop (a)) runs the game 10 times - or in the game, this is the 10 coins that drop down and the player has to catch.
(C) The inner white loop (game loop (b)) is the main game - a coin falls down and the player has to get underneath to catch it. Once the coin reaches the bottom, the blue loop repeats this game loop until 10 coins have been dropped.
(D) The last section of the game prints the players score - how many coins were caught out of the 10 that dropped, then waits for a key to be pressed before starting a new game.
T is a variable that is going to keep track of our score, and P is the horizontal position of our player on screen. In here, we're setting both to a starting value of 0. But in line 20, why not just type LET T=0 rather the use LET T=P? Code takes up precious memory in the computer. Because of the lack of it (1K), using a variable to set another saves memory (when you've only got 1024 every byte counts).
As far as the technical explanation goes, a numeric value like "0" is stored in the computer as a binary representation based on its value, and the number of digits (ie. the text characters) entered... This could be up to 6-8 bytes of memory.
Referencing another variable (P) means no need to store a value - instead just storing the reference to the P variable which takes less memory. Yes - the 1980's were a very primitive time for computing, and we're talking about things here that you will mostly never-ever have to even think about these days.
Alastair Gourlay, the author of this book, has a section at the back of it that lists a LOT of optimisation tips such as this. Valuable sage advice for that time!
(Note that the BASIC commands (like LET) were stored as a single byte - a numeric value that represented the command - rather then the 3 characters of the command. The Sinclair BASIC language had a keyword mode you would use to enter these (usually the first thing BASIC would do, only allowing numbers to be entered first), where all the available program commands were accessed through a single key. That's why the keyboard of these machines appeared to have a lot of commands and symbols printed on them)
The game is all about catching falling coins. Its really a game of getting "best out of 10", and this loop here repeats the game 10 times.
When we start each round, we start by clearing the screen using CLS (CLear Screen)
This line prints the score stored in T into a character location of the screen. The command AT was followed by the vertical(row) and horizontal(column) positions. In this case, 12 characters down, and 0 characters (the left side of the screen) across.
R is a random value between 0 and 17. As with todays languages, INT forces the value to be a whole number and not a floating point number. And as this game is all about catching one coin at a time, the coin falling down is done by looping from top to bottom (10 characters is how far a coin will travel). In a way, this is a game-loop within a game loop.
The first line inside this Y loop is to draw a coin (the letter "O") on screen, at Y (vertically - starting at the top) and R (our random location across the screen)
N is a new variable that will be used to work out the new position of our player on screen, alongside P which stores the previous location. Sounds confusing perhaps, but once we get further down in the code, it will probably start to make more sense. Likewise, instead of using a +/- value here, instead we're relying on the True/False value (which is of course, 1 or 0) of a key check (the command INKEY$ checks if a key has been pressed). Without using typed-in numbers in our code, we're saving a few bytes of precious memory...
If 4 pressed, move P + (1 (True)) - (0 (False)). That add's 1 to our position
If 1 pressed, move P + (0 (false)) - (1 (True)). That subtracts 1 from our position
N contains the new location of the player. This line simply tests the location is within the playing area. If it goes outside left or right then set N back to P (previous location).
The first AT deletes the players graphic by printing 2 spaces over it. The next AT prints the players graphic (2 graphical characters) at its new location (N) and the last AT deletes the coin from the screen at its current location, ready to be moved and redrawn.
(Unlike modern systems like pygame where all the drawing is done on a surface in memory before its updated to the screen, all screen printing and drawing is done directly - think of the screen as being a "live update". We delete the players graphic first by printing 2 spaces to blank the previous one out. We then quickly print the player at the new location. After that - as we've already seen the coin printed a few lines above - now we have to delete it from screen as we're going to move it and then print it at a new location.
If we don't delete it, we'll get a line of the letter "O" running down the screen. This is why you'll often see old BASIC games have fairly flickery graphics - even more flickery if the machine is very slow).
We'll copy the N location into P (to make it the previous location - remembering that N will of course be recalculated again to move it if a key was pressed)
And we jump back and repeat our Y loop. The loop essentially draws the coin coming down the screen until it reaches the bottom. While this happens, the player can move left and right to position it underneath to catch the coin.
Once the Y loop has completed, the code continues... This is another of those ways to save bytes by using variables and a little math to add 1 to the score if the character was underneath the coin. Like the INKEY$ calculation used for moving the character, we're again using a boolean (true/false) to add a point to the score. P is of course our position on screen for the player. R is the horizontal location of the coin... Because the player graphic is 2 characters wide, we simply need check whether the coin was lined up with the first or second character to score a point (ie. +1 (if it was True))
We now roll back up to our next coin drop. And it all happens again 10 times...
Once we're finished the loop, we print the players score out of 10. PAUSE 4E4 makes the code pause and wait for a key press, and RUN just executes the BASIC program again (RUN is of course the command for running a program)
In part 3, I'm going to finally show you how I would re-write this in python, using of course pygame to relive the excitement of a 12 year old, some 33 years ago...
After that, you can grab your mobile devices, play some flappy-roads, or crossy-bird games (with gameplay that is literally no more complex then something from the 80's) and journey back to 2015.
See you soon...
We're going to break down an old 1k ZX81 game written in BASIC in 1982. This code is a scan I made from the first-ever book I bought... "34 Amazing Games for the 1K ZX81" by Alastair Gourlay. It taught me a load of techniques that would have taken a long time to pick up on my own, such as reading keys and drawing characters moving on screen.
In the books foreword, written by Tim Hartnell (a name well known in the 80's when it came to programming books), it's pretty clear that the aim of the book is more then just a collection of games to play...
You'll probably want to enter the programs just as they are the first time you read through the book. We'd like you to adapt them, improve and modify them to put your own stamp on them. And we're sure the programming tricks Alastair has used to compress the programs into 1K can be used by you in other programs.
This book is much more then just a collection of games. It is a guidebook to show you how to make the most of your 1K ZX81
The game - DROPOUT, catching coins
Lets take a look at what's going on here.
This game is, like most of that era (and for that little memory) very simple. The player gets the chance to catch a coin that drops from the top of the screen (each time positioned randomly across the screen) ten times. The aim of the game is to get the most catches out of 10.Its simple, but when you compare the game play to something like the runner game concept that is all over the mobile market where the aim is to just collect as many items while avoiding the obstacles, the concept of simplicity is really no different. The appeal comes in the whole 'one more go to get a better score' of what is a very basic game play model.
Breaking down the code into blocks, there are 4 things that are happening in here.
(A) We initialise the game by resetting the score, and setting the player starting position.
(B) This is followed by the game in not one, but two loops... The blue loop (game loop (a)) runs the game 10 times - or in the game, this is the 10 coins that drop down and the player has to catch.
(C) The inner white loop (game loop (b)) is the main game - a coin falls down and the player has to get underneath to catch it. Once the coin reaches the bottom, the blue loop repeats this game loop until 10 coins have been dropped.
(D) The last section of the game prints the players score - how many coins were caught out of the 10 that dropped, then waits for a key to be pressed before starting a new game.
Onto the code...
So what is happening here... What does all that BASIC code do, and how does this game work? I'll break down the code into small snippets and explain what each is doing. Once we've got the whole thing broken down, we'll be ready to jump forward to modern day python and re-create this 33 year old game in mear seconds... Eh, maybe more like between 10-20 minutes...Is BASIC really basic? Sure is...
T is a variable that is going to keep track of our score, and P is the horizontal position of our player on screen. In here, we're setting both to a starting value of 0. But in line 20, why not just type LET T=0 rather the use LET T=P? Code takes up precious memory in the computer. Because of the lack of it (1K), using a variable to set another saves memory (when you've only got 1024 every byte counts).
Memory? Limits? What?
Today, this level of "optimisation" doesn't really make much sense I'm sure - after all, machines have gigabytes of memory.As far as the technical explanation goes, a numeric value like "0" is stored in the computer as a binary representation based on its value, and the number of digits (ie. the text characters) entered... This could be up to 6-8 bytes of memory.
Referencing another variable (P) means no need to store a value - instead just storing the reference to the P variable which takes less memory. Yes - the 1980's were a very primitive time for computing, and we're talking about things here that you will mostly never-ever have to even think about these days.
Alastair Gourlay, the author of this book, has a section at the back of it that lists a LOT of optimisation tips such as this. Valuable sage advice for that time!
(Note that the BASIC commands (like LET) were stored as a single byte - a numeric value that represented the command - rather then the 3 characters of the command. The Sinclair BASIC language had a keyword mode you would use to enter these (usually the first thing BASIC would do, only allowing numbers to be entered first), where all the available program commands were accessed through a single key. That's why the keyboard of these machines appeared to have a lot of commands and symbols printed on them)
Lets continue...
The game is all about catching falling coins. Its really a game of getting "best out of 10", and this loop here repeats the game 10 times.
When we start each round, we start by clearing the screen using CLS (CLear Screen)
This line prints the score stored in T into a character location of the screen. The command AT was followed by the vertical(row) and horizontal(column) positions. In this case, 12 characters down, and 0 characters (the left side of the screen) across.
R is a random value between 0 and 17. As with todays languages, INT forces the value to be a whole number and not a floating point number. And as this game is all about catching one coin at a time, the coin falling down is done by looping from top to bottom (10 characters is how far a coin will travel). In a way, this is a game-loop within a game loop.
The first line inside this Y loop is to draw a coin (the letter "O") on screen, at Y (vertically - starting at the top) and R (our random location across the screen)
Letter O? Where's the graphics?!
In the ZX81, there really were almost no graphics capabilities other then some custom graphical characters and the ability to plot a chunky block to a location on screen (something like 64 x 44 blocks (what they called "pixels" but at a (very) low-resolution)). Drawing blocks one at a time was SLOW - so this is why so many games you would use text for their graphics elements. That is why the letter O is used to represent a coin.Nuff said, what's next...
N is a new variable that will be used to work out the new position of our player on screen, alongside P which stores the previous location. Sounds confusing perhaps, but once we get further down in the code, it will probably start to make more sense. Likewise, instead of using a +/- value here, instead we're relying on the True/False value (which is of course, 1 or 0) of a key check (the command INKEY$ checks if a key has been pressed). Without using typed-in numbers in our code, we're saving a few bytes of precious memory...
Run me through this... Hows that work?
Moving the player left and right by one character at a time just means adding or subtracting 1 from its horizontal value (which is currently being stored as N). Only one key can be pressed at a time, so the maths are fairly simple.If 4 pressed, move P + (1 (True)) - (0 (False)). That add's 1 to our position
If 1 pressed, move P + (0 (false)) - (1 (True)). That subtracts 1 from our position
Don't fall off the screen!
N contains the new location of the player. This line simply tests the location is within the playing area. If it goes outside left or right then set N back to P (previous location).
Drawing the graphics, ahem, characters...
In Sinclair BASIC, multiple print location's can be placed in one line by separating them with semi-colons (;). Rather then 3 lines (of course, which would consume memory) we can do three things in one.The first AT deletes the players graphic by printing 2 spaces over it. The next AT prints the players graphic (2 graphical characters) at its new location (N) and the last AT deletes the coin from the screen at its current location, ready to be moved and redrawn.
(Unlike modern systems like pygame where all the drawing is done on a surface in memory before its updated to the screen, all screen printing and drawing is done directly - think of the screen as being a "live update". We delete the players graphic first by printing 2 spaces to blank the previous one out. We then quickly print the player at the new location. After that - as we've already seen the coin printed a few lines above - now we have to delete it from screen as we're going to move it and then print it at a new location.
If we don't delete it, we'll get a line of the letter "O" running down the screen. This is why you'll often see old BASIC games have fairly flickery graphics - even more flickery if the machine is very slow).
Update the players previous position
We'll copy the N location into P (to make it the previous location - remembering that N will of course be recalculated again to move it if a key was pressed)
Make the coin fall...
And we jump back and repeat our Y loop. The loop essentially draws the coin coming down the screen until it reaches the bottom. While this happens, the player can move left and right to position it underneath to catch the coin.
Did I win the game? Did I, huh, did I???
Once the Y loop has completed, the code continues... This is another of those ways to save bytes by using variables and a little math to add 1 to the score if the character was underneath the coin. Like the INKEY$ calculation used for moving the character, we're again using a boolean (true/false) to add a point to the score. P is of course our position on screen for the player. R is the horizontal location of the coin... Because the player graphic is 2 characters wide, we simply need check whether the coin was lined up with the first or second character to score a point (ie. +1 (if it was True))
We now roll back up to our next coin drop. And it all happens again 10 times...
Lets play again...
Once we're finished the loop, we print the players score out of 10. PAUSE 4E4 makes the code pause and wait for a key press, and RUN just executes the BASIC program again (RUN is of course the command for running a program)
Now you're a BASIC wizz-kid!
Sure - the games simple - but for 1982 it was a serious learning exercise and lots of fun (given there was nothing else to compare it to). If you're already savvy with python programming then you have probably already re-written that game in your head without much effort.In part 3, I'm going to finally show you how I would re-write this in python, using of course pygame to relive the excitement of a 12 year old, some 33 years ago...
After that, you can grab your mobile devices, play some flappy-roads, or crossy-bird games (with gameplay that is literally no more complex then something from the 80's) and journey back to 2015.
See you soon...
BASIC games in Python - 1982 returns (Part 1)
Posted on 00:03 by kevman3d
One thing I always enjoyed back in the 80's, and what made computing just so much fun was
the ability to write my own software - in particular - Games.
It was not just a lot of fun writing the games themselves, it was the exploration of what
the computer was capable of doing through code. Home computing was all new - the wild west of computer gaming. It was about invention, discovery and mastering the machine and its programming language.
Nothing had really been invented yet. No optimised game engines, no hardware accelerated
graphics and sound. You had limited resources when it came to memory and CPU speeds,
which meant some pretty creative ways to make things happen...
Now - some 32+ years later along comes the modern age of computing. Mobile devices are the
new gaming platform. Tools to develop games are everywhere - hardware and software has been
honed for performance and the capabilities of the hardware and software are well documented.
There's no real need to invent your own graphics and sound routines, and even some applications
don't need you to understand how to program a computer at all - click a few options, drag in a
few resource file and be creative with the logic - viole! An awesome game without the technical
requirements pops out.
But I love to code - and I like the simplicity that BASIC used to give... In fact, I really
enjoyed playing some of those old typed-in games that I was inspired to code them again. And that's what this post is all about...
However, while BASIC is a relatively standard language, each machine had a different 'flavor', or 'dialect' of it which made code incompatible across the range of computers of that time.
BASIC could do a lot (it had to - operating the computer relied on it being able to do stuff!), but compared to modern programming languages it was, eh, basic.
It really is (for me at least) the home computing language for the modern age. Its easy to learn - like BASIC was. Its more human readable then most other languages (such as Java or C++/C#). The best part for me is that its a lot of fun to program with as well. This makes it the perfect language (imho) for those new to coding.
Unlike the BASIC days, Python is more standardized - its the same across various platforms - you write a game on Windows, its likely it will work on an Apple Mac, Linux and even on that little Raspberry Pi you may have bought to play around with at home.
But Python has one thing that makes it an even better language for gaming, and that is its modular nature. Python has all the bells and whistles of a standard language, but additional functions and features can be imported to extend it even further extremely easily...
To write games, then there's one free module you need to get...
You can download the installer for it from pygame.org
I also like pygame because not only does it extend the base Python language with a huge library of game-related features, the way games are programmed is literally the same as traditional BASIC games. In fact, they're so similiar that it does make translating a BASIC program to Python very easy.
We start by importing the modules we need. Obviously you should install Pygame before you get this far - but I'm sure you already know that...
(Note that I import the sys module mostly for its command sys.exit() - this is needed to help ensure that any game we're running gets properly shut down by Windows)
Next, we initialise pygame itself to make sure its all loaded in and ready to run.
We'll want to control the speed the game runs at of course - if we're running on a super-fast system then things can of course scream along too quickly. For that, we create a Clock() object that takes care of maintaining the programs playback speed.
Note : gameWindow technically defines a surface, (imagine a blank image in memory). This surface represents the pixel contents of our game window that we see on the screen. When we want to place things 'on screen', we can draw, or blit them onto this surface. Because this surface is in memory, this drawing takes place in the background.
Once we're inside this loop, this is where the fun happens. We take care of erasing graphics, moving objects, checking for collisions and redrawing graphics back to our window. We can call functions from inside this game loop to show title screens, update levels, and so forth. Since this is a generic structure example, I've got no code here (yet) - just know that we'll be developing our game here - honest...
In the example above, I've asked a QUIT event to call a function called exitGame() where I will setup code that will cleanly close the game down.
(Note here that I'm referring to pygame.locals.QUIT for the event name. We could load the locals into the root namespace of our program and just refer to locals using the event name (in this case, I could have just used the word QUIT without the pygame.locals namespace...). This is easily done at the start of the script by importing the module using from pygame.locals import * )
Finally, at the very end of this game loop, we can use our Clock we created to force the loop to run at a specific speed in frames-per-second. In this example, I'm forcing the game to run at 24 frames per second. For an 80's game, this would be even slower since we'd be forcing the game to run as though its taking time to process on a slow CPU.
And that in a nutshell is what makes for a generic pygame program structure. We just need to fill in the gaps of course with our actual game code, but with this in place, we're ready to roll...
pygame.quit() unloads the pygame system, and sys.exit() makes sure the python game window is shut down properly. Without sys.exit(), pygame.quit() will produce an error... So using both is usually required.
See you in the part 2...
the ability to write my own software - in particular - Games.
It was not just a lot of fun writing the games themselves, it was the exploration of what
the computer was capable of doing through code. Home computing was all new - the wild west of computer gaming. It was about invention, discovery and mastering the machine and its programming language.
Nothing had really been invented yet. No optimised game engines, no hardware accelerated
graphics and sound. You had limited resources when it came to memory and CPU speeds,
which meant some pretty creative ways to make things happen...
Now - some 32+ years later along comes the modern age of computing. Mobile devices are the
new gaming platform. Tools to develop games are everywhere - hardware and software has been
honed for performance and the capabilities of the hardware and software are well documented.
There's no real need to invent your own graphics and sound routines, and even some applications
don't need you to understand how to program a computer at all - click a few options, drag in a
few resource file and be creative with the logic - viole! An awesome game without the technical
requirements pops out.
But I love to code - and I like the simplicity that BASIC used to give... In fact, I really
enjoyed playing some of those old typed-in games that I was inspired to code them again. And that's what this post is all about...
What is BASIC?
Its the standard language that came with every home computer. It was chosen because it was a relatively simple language to learn (in comparison to others around). One machine (the Jupiter Ace) came with Forth rather then BASIC, but it didn't take off as much as the others.However, while BASIC is a relatively standard language, each machine had a different 'flavor', or 'dialect' of it which made code incompatible across the range of computers of that time.
![]() |
| Well, hello there to you too, Mister BASIC! |
So - tell me more of this BASIC you speak of...
BASIC stands for Beginners All-purpose Symbolic Instruction Code. Unlike modern languages like Python, it was linear - written line-by-line, each one starting with a line number - each line of code would be executed from one line to the next (though that's how most code works of course - line by line - however without the numbering). Below is a very simple example of a BASIC program.
10 REM This is a program that says hello to you
20 PRINT "What is your name?"
25 INPUT A$
30 PRINT "Hello "; A$
40 IF A$="Kevin" THEN GOSUB 100
50 GOTO 20
100 REM A subroutine to celebrate
110 PRINT "Awesome Name! Lets Celebrate!"
120 FOR A=1 TO 100
130 PRINT "Hip-hip-hooooray! ";
140 NEXT A
150 RETURN
20 PRINT "What is your name?"
25 INPUT A$
30 PRINT "Hello "; A$
40 IF A$="Kevin" THEN GOSUB 100
50 GOTO 20
100 REM A subroutine to celebrate
110 PRINT "Awesome Name! Lets Celebrate!"
120 FOR A=1 TO 100
130 PRINT "Hip-hip-hooooray! ";
140 NEXT A
150 RETURN
There was more to it then just writing programs
These commands were also typed into the computer directly (without line numbers) to operate the computer itself and make it do stuff. For example, To save a BASIC program, you'd have to type the command SAVE "filename". To display something on screen, you'd type PRINT "some text". To reset the computer, you could just type NEW and it would clear the memory. That said, sometimes you'd just switch the machine off and back on instead to do that!BASIC could do a lot (it had to - operating the computer relied on it being able to do stuff!), but compared to modern programming languages it was, eh, basic.
PYTHON
There's one language I feel makes coding as much fun as it was back then, and that's Python.It really is (for me at least) the home computing language for the modern age. Its easy to learn - like BASIC was. Its more human readable then most other languages (such as Java or C++/C#). The best part for me is that its a lot of fun to program with as well. This makes it the perfect language (imho) for those new to coding.
Unlike the BASIC days, Python is more standardized - its the same across various platforms - you write a game on Windows, its likely it will work on an Apple Mac, Linux and even on that little Raspberry Pi you may have bought to play around with at home.
But Python has one thing that makes it an even better language for gaming, and that is its modular nature. Python has all the bells and whistles of a standard language, but additional functions and features can be imported to extend it even further extremely easily...
To write games, then there's one free module you need to get...
Pygame
Pygame is awesome... Its a game development suite that contains all the commands and features you need to draw graphics, play sounds and read keys/mouse when it comes to gaming.You can download the installer for it from pygame.org
I also like pygame because not only does it extend the base Python language with a huge library of game-related features, the way games are programmed is literally the same as traditional BASIC games. In fact, they're so similiar that it does make translating a BASIC program to Python very easy.
A look at the basic structure of a pygame program
Setting up a simple base program to work with pygame is very easy and goes something like this.We start by importing the modules we need. Obviously you should install Pygame before you get this far - but I'm sure you already know that...
import pygame
import pygame.time
import pygame.locals
import sys
import pygame.time
import pygame.locals
import sys
(Note that I import the sys module mostly for its command sys.exit() - this is needed to help ensure that any game we're running gets properly shut down by Windows)
Next, we initialise pygame itself to make sure its all loaded in and ready to run.
pygame.init()
We'll want to control the speed the game runs at of course - if we're running on a super-fast system then things can of course scream along too quickly. For that, we create a Clock() object that takes care of maintaining the programs playback speed.
fpsClock = pygame.time.Clock()
Give me something to look at
Games are just no fun if you can't see them on screen. We next create a window for our game to be displayed in. In this example, I'm making a 640 x 480 pixel window.
gameWindow = pygame.display.set_mode((640,480))
Note : gameWindow technically defines a surface, (imagine a blank image in memory). This surface represents the pixel contents of our game window that we see on the screen. When we want to place things 'on screen', we can draw, or blit them onto this surface. Because this surface is in memory, this drawing takes place in the background.
Lets play!
And here's where the magic happens - the game loop itself. Basically the game will repeatedly run until we force an exit from the game. We create the game inside a while loop - in this case, an infinite one...
while True:
Once we're inside this loop, this is where the fun happens. We take care of erasing graphics, moving objects, checking for collisions and redrawing graphics back to our window. We can call functions from inside this game loop to show title screens, update levels, and so forth. Since this is a generic structure example, I've got no code here (yet) - just know that we'll be developing our game here - honest...
Continuing on...
Next thing is that we should also check for any events - that is, asking pygame to let us know if something occured such as the mouse was clicked, a key was pressed or some other type of event like the user closing the window (essentially quitting the game). This is done by iterating through a list provided by the function pygame.event.get() and checking if a particular event has occured. The pygame.locals contains a collection of constants that allow us to check events by some name, rather then an obscure numeric value...
for event in pygame.event.get():
if event.type == pygame.locals.QUIT:
exitGame()
if event.type == pygame.locals.QUIT:
exitGame()
In the example above, I've asked a QUIT event to call a function called exitGame() where I will setup code that will cleanly close the game down.
(Note here that I'm referring to pygame.locals.QUIT for the event name. We could load the locals into the root namespace of our program and just refer to locals using the event name (in this case, I could have just used the word QUIT without the pygame.locals namespace...). This is easily done at the start of the script by importing the module using from pygame.locals import * )
Anyway...
Once we're done doing the biz of drawing and moving, we just need to tell pygame to update the game window. This will copy the contents of our gameWindow surface from memory back to the screen.
pygame.display.update()
Finally, at the very end of this game loop, we can use our Clock we created to force the loop to run at a specific speed in frames-per-second. In this example, I'm forcing the game to run at 24 frames per second. For an 80's game, this would be even slower since we'd be forcing the game to run as though its taking time to process on a slow CPU.
fpsClock.tick(24)
And that in a nutshell is what makes for a generic pygame program structure. We just need to fill in the gaps of course with our actual game code, but with this in place, we're ready to roll...
Before I finish up
What about that exitGame() function I made my QUIT event call? Well, its as simple as this...
def exitGame():
pygame.quit()
sys.exit()
pygame.quit()
sys.exit()
pygame.quit() unloads the pygame system, and sys.exit() makes sure the python game window is shut down properly. Without sys.exit(), pygame.quit() will produce an error... So using both is usually required.
Phew! That's a lot to take in...
I'll end this article before it becomes immensely long, and let you digest that information. If you've gotten this far, hopefully you're getting the buzz that you're keen to start writing your own games... In part 2, I'll look at how we can reverse-engineer an old BASIC game from a ZX81 1kb listing and translate that to Python... We'll attempt to keep the whole old-school look and style - so groovy retro graphics and all...See you in the part 2...
Subscribe to:
Posts (Atom)









