Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 05:46 02 Aug 2025 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : Game programming

Author Message
Plasmamac

Guru

Joined: 31/01/2019
Location: Germany
Posts: 579
Posted: 03:36pm 28 Jan 2021
Copy link to clipboard 
Print this post

hi, I am currently trying to develop a game for the cmm2. I have to move many graphics of different sizes as quickly as possible. does someone have a tip how to program it the fastest? I use 1023/768 mode 9.
I am currently using sprites with .png.
Plasma
 
William Leue
Guru

Joined: 03/07/2020
Location: United States
Posts: 405
Posted: 03:42pm 28 Jan 2021
Copy link to clipboard 
Print this post

It all depends on what you want to do.

If you are moving a lot of things around on the screen and having to do collision detection, then sprites are your friend.

On the other hand, stuff like scrolling the screen or just animating a few things without flicker, then just do offscreen drawing to a page other than page 0, then copy the offscreen page to page 0 using the B option to sync up with the refresh rate.

Both these things can be combined of course. Read Peter Mather's document on advanced graphics, it pretty much tells the story.

-Bill
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4311
Posted: 03:53pm 28 Jan 2021
Copy link to clipboard 
Print this post

You may also be in the wrong mode. I'm no expert on games but from other posts most people are using a lower-res mode, possibly because in BASIC the CMM2 just can't move enough pixels around fast enough in the hi-res modes, and/or you get more pages to play with in the low-res modes.

Tom
MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
RetroJoe

Senior Member

Joined: 06/08/2020
Location: Canada
Posts: 290
Posted: 04:16pm 28 Jan 2021
Copy link to clipboard 
Print this post

Indeed, the fundamental technique appears to be "compositing" the graphic images on a hidden page (typically Page 1), then copying them to the display page (Page 0) in the "right way, at the right time".

Per Tom's comment, using display modes with lower resolution and bit depth will dramatically increase performance and give you more offscreen pages to work with.

From what I've seen, sprites are not as widely used as yet, but have the obvious advantages of living "on top" of Page 0 i.e. taking care of preserving the underlying pixels as they move around, as well as being more object-oriented e.g. knowing where they are in the X,Y plane and if / what they collided with.

Strongly recommend you read the CMM2 Graphics Manual (included in the firmware ZiP file on Geoff's home page), as well giving this blog post  a browse (and ideally adding to it!)

Happy Maxi-miting!!
Edited 2021-01-29 02:27 by RetroJoe
Enjoy Every Sandwich / Joe P.
 
MauroXavier
Guru

Joined: 06/03/2016
Location: Brazil
Posts: 303
Posted: 05:19pm 28 Jan 2021
Copy link to clipboard 
Print this post

For a very fast action game, I recommend 320x200 or 320x240, with last firmware version, using 8 or 16-bit colour will not give too much speed difference, but lower colour depth will give more video pages if you need.

For normal to a fast action game, you can use 640x400 or 640x480 mode, but in this case, the colour depth will make some difference in speed. Games too much demanding could get be hard to develop in this mode, like shooting up bullet hell style with 300 entities on the screen at the same time, for example.

Modes like 800x600 or even 1024x768 you can make games that don't have the scroll, like Lode Runner, or you can try make some scroll, but in this case with not be so smooth to keep the game running at the same time. Games that scroll the full screen only when you go to the screen limits is a good approach, like King's Valley (MSX).

Use the 1024x768 mode to animate too many objects at the same time on the screen can be a hard task to the CPU, as this video mode consumes a lot of the bus.

And remember, not only the video pages but the command FRAMEBUFFER always is your friend to make things easier when programming games.
 
Nelno

Regular Member

Joined: 22/01/2021
Location: United States
Posts: 59
Posted: 04:47am 29 Jan 2021
Copy link to clipboard 
Print this post

It really depends what you're trying to do, which is, I think, a large part of what MauroXavier is saying above. Despite CMM2 being fast, you're still going to have to make some decisions up front to achieve best results, and spend some time optimizing. Despite the draw of having "easy mode" sprite programming, etc., I think it might get boring if there weren't any optimization challenges.

Let me caveat all of the following with the fact that I've been messing around with CMM2 for a bit less than two weeks, on and off. I've spent a lot of that time investigating how to reliably hit vsync, what a decent game loop will look like, what my performance margins are like (and why my SD card wasn't working), but I definitely do not know everything there is to know. Feel free to call out my mistakes or challenge my assumptions. My goal here is to make some games and make them well.

page copy

First thing to know is the page copy command. In general, if you're in one of the 8-bit modes you can probably get away with this at up to 800x600. In the 16-bit and especially 12-bit modes, it's going to require a lower resolution.

From my tests, 1024x768, 8-bit mode is already too slow to use the page copy technique.

If you run the following commands from the prompt:

mode 9,8:timer=0:page copy 1 to 0:? timer

You'll see the time it will take to do a single page copy from hidden page 1 to page 0.

For me this is 14.714 ms.
In mode 9,16 it is 45.401 ms!
12-bit mode isn't available in mode 9.

Since mode 9 is 60 Hz, and assuming you're okay with 8-bit mode, that means you have a total of ~16.6 ms per frame to do all of your game logic and rendering. If you want to avoid any screen tearing, you cannot render for almost 15 of 16.6 ms per frame!

Your only real option then is to drop down to 30Hz, at which point you'll have 33.3 ms per frame. Note that with a 45 ms copy in 16 bit mode, you're not going to be able to manage even 30 FPS, but you could get away with 15 FPS.

Some games are probably fine at 15 FPS. For instance, if you wanted to do an Ultima-like RPG, that would work fine.

I hope we can get the option to swap the visible page back in MMBASIC, vs. always having to copy. Copy is certainly the easier method to use and probably should be used in most cases, but I can imagine how if you really wanted to tweak performance out of the higher res modes, page swapping might be your only hope, and even then it would probably not be as generally applicable as page copy is.

sprite load
Second thing, and this is really more of a technique that wasn't entirely obvious to me how I should do it from the get go, is loading your sprite data onto the hidden graphics pages. You do this as follows:


page write <page you want to store sprites on>

load png <filename>,x,y

page write <page you want to draw on>

' when you want to set a sprite to an image:

sprite read,<sprite num>,x,y,width,height,<sprite page>


Of course, sprite show, sprite next, etc. still need to happen as necessary.

How you update your sprites depends on your games needs. If you absolutely must update all sprites each frame, and you're also modifying your background, then you probably want to do:


sprite hide all

' change background as you need

' position your sprites with sprite next

sprite restore ' show and update all of the sprites


That's the simple loop. It may vary for you based on what you're trying to do.

Optimize
I'm still learning what works on the CMM2. There are things specific to BASIC that throw some of my tried-and-true optimization knowledge out the window.

First, time everything. I recommend having some timers visible (frame rate, game logic time, render time, etc.) each time you run the game. This way, if you add something and suddenly a time goes up significantly or you're not hitting frame rate, you'll have a better chance of catching it sooner rather than later. I haven't tried the new profiling capabilities yet, but these should be tremendously helpful.

Second, remember you're writing code in BASIC and it's interpreted. Despite the fact that CMM2 is very fast at executing BASIC, this can and does have significant effects. In my tests, calling functions from a loop, even one that just iterated 32-64 times, was much slower than just inlining the code in the loop, but it's going to vary depending on what's in your function as to how much overhead it's costing you. Again, timing is your friend.

Third, don't take what you know for granted. I tried some techniques like packing data into arrays that on most modern architectures would pay off, but which backfired. For instance, if you have 4096 objects which you're tracking the position of, you might start off by storing their positions in two arrays, one array for x values, one for y like this:


const MAX_STARS = 4096
dim integer stars_x(MAX_STARS)
dim integer stars_y(MAX_STARS)


Note that trying to update 4096 stars a frame is going to be a bad idea, but if you can amortize the cost and go through some number of them each frame, that may be okay. I recommend NOT skipping frames and doing all 4096 every n frames. Consistent framerates are important for action / arcade games. Better to run a little slower every frame then have spikey / erratic frame rates.

Anyway, the issue with two arrays like this is normally cache locality. You need to access both x and y values for star i at the same time, but star_x(i) and star_y(i) are separated by at least 4096 * 8 bytes in memory. Since I'm still learning about the CMM2 architecture, I can't say exactly how this relates to the cache / cache lines, but often, you'll get a speedup by putting values that are accessed together, in memory together. So, in this example:


const MAX_STARS = 4096
dim integer stars_pos(MAX_STARS)

local integer i = 0
for i = 0 to MAX_STARS step 1
 local integer x = stars_pos(i * 2)
 local integer y = stars_pos(i * 2 + 1)
 ' do something with x and y
next i


Here you have achieved better cache locality for the cost of 2 multiplies and an addition, which are generally very fast operations.

However (and to be clear, this is just an example and not exactly my test, so timing this might surprise), this did not work out. Not only did I find this didn't speed anything up, it was slower in MMBASIC. A bit more testing indicated that the main cost is probably the interpreter. As you add lines and operations into the loop you pay a high cost in performance -- much more than you would in a compiled language, i.e. C/C++. To caveat this, 4096 * 8 is only 32KB, so this wasn't a great test for cache locality, but it did once again highlight the cost of extra interpreted lines in a loop. If you think about what an interpreter is doing for each line, it's probably just invalidating most of the cache lines anyway and there's probably not a whole lot of benefit to optimizing array access.

Again, this is just an example -- don't take this for gospel, and go back and reference the first point under this heading: time everything (at least when you suspect it can / should / needs to be faster).

I'm sure there is a lot more to learn!
Edited 2021-01-29 14:51 by Nelno
 
RetroJoe

Senior Member

Joined: 06/08/2020
Location: Canada
Posts: 290
Posted: 05:51am 29 Jan 2021
Copy link to clipboard 
Print this post

Wow, another "winner" thread for 2021 !

I will remind everyone about Peter's recently added INC (INCrement) command, which optimizes the interpreter for simple variable increments / decrements like game scorekeeping and "shots fired" :)

Mostly off-topic, but it's been nagging me for a while, and this seems like as good a place and time to ask.

Why did it become a convention to declare subroutines and functions at the end of your code listing, rather than the beginning?

We routinely declare everything else at the top of our code listings: variables, includes, directives, options, headers, libraries, object definitions, etc. etc.

This convention is logical and expository - it's telling the interpreter or compiler, but also the human reader, "Here is all the stuff I will be using in my program".  It's like the overture in an opera, introducing you to the musical themes and motifs you will be hearing.

Yet, when it comes to the most important declarations and "preview" of how your code is structured i.e. subroutines and functions, they are hidden away at the tail end of the code like a dirty secret, and you have to make an explicit "trip" to read them.

Any code appearing inside subroutine and function declarations is skipped unless your main code loop invokes them, so where did this convention originate? Is it from the days when procedural code referenced literal line numbers, and appending lines to the end of a code listing was much easier than inserting them?
Edited 2021-01-29 15:51 by RetroJoe
Enjoy Every Sandwich / Joe P.
 
lizby
Guru

Joined: 17/05/2016
Location: United States
Posts: 3378
Posted: 01:25pm 29 Jan 2021
Copy link to clipboard 
Print this post

  RetroJoe said  Why did it become a convention to declare subroutines and functions at the end of your code listing, rather than the beginning?

Personally, I like to have my main loop at the very top, after variable definitions.

I vaguely recall some language or implementation which required that functions be placed before the main loop so that the compiler/interpreter would know where to go when it encountered a call to the routine later, but this always seemed awkward to me, harking back to the day when programmers catered to the hardware, rather than having software (compilers and interpreters) which catered to the programmer.
PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4311
Posted: 01:42pm 29 Jan 2021
Copy link to clipboard 
Print this post

  lizby said  
  RetroJoe said  Why did it become a convention to declare subroutines and functions at the end of your code listing, rather than the beginning?

Personally, I like to have my main loop at the very top, after variable definitions.

I vaguely recall some language or implementation which required that functions be placed before the main loop so that the compiler/interpreter would know where to go when it encountered a call to the routine later, but this always seemed awkward to me, harking back to the day when programmers catered to the hardware, rather than having software (compilers and interpreters) which catered to the programmer.


Subroutines/functions at the end is the result of the common "top-down" practice for reading/writing code, IMHO it makes it easier to just focus on what you are interested in rather than having to wade through all the low-level code. For strict adherance a sub/func is declared "immediately" after it is first required.

@RetroJoe looks like you might be an adherent of "bottom-up" practice, personally I think of that as a legacy of languages like "C" which expect at least declarations before usage, but YMMV.

Of course as soon as you start using #Include you end up with a mix of both ... that is if you favour the "standard" of putting all the #Include's at the top rather than sprinkling them like magic stardust throughout the code.

Best wishes,

Tom
MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
jirsoft

Guru

Joined: 18/09/2020
Location: Czech Republic
Posts: 533
Posted: 01:48pm 29 Jan 2021
Copy link to clipboard 
Print this post

I'm also using the structure:


INCludes
CONSTANTs and DIMs
main program (loop)
END

FUNCTIONs and SUBs


The main reason is that I'm usually working on method (SUB/FUNCTION), which I have on total end for quick access- fastest way of jump to is on begin or end of the file (HOME/END keys) and on begin are still needed some decalarations etc., so end is the next best choice.
When I'm done, I move next method to the end...
Jiri
Napoleon Commander and SimplEd for CMM2 (GitHub),  CMM2.fun
 
Nelno

Regular Member

Joined: 22/01/2021
Location: United States
Posts: 59
Posted: 02:18am 30 Jan 2021
Copy link to clipboard 
Print this post

  RetroJoe said  
Why did it become a convention to declare subroutines and functions at the end of your code listing, rather than the beginning?


I don't know if it's convention for me yet, but I have been tending to do this in MMBASIC for some reason. It also occurred to me that it was a little weird because the code that is calling the subroutines is doing it before they're declared.

However, if the interpreter knows enough to know the subroutine is declared without interpreting it first, this is fine with me.

One way of looking at this is that the main loop is generally much closer to the entry point of the program, so in a logical flow sense, it's usually executed first. I have been putting my subroutines generally in order of execution, so, for instance, my swapPages routine that is referenced from my mode call before I get into the game loop, is in front of the game loop (in some of my programs).

But I think there's a more practical reason for me. I seem to be spending more time up at the top, messing around in the game loop, only occasionally paging down to add or modify a subroutine. But... I haven't written many large subroutines in my testing so far, so this may change. I am finding the paging around to be a bit annoying already. A "Browse to Subroutine" function like QBasic had could be nice.
 
RetroJoe

Senior Member

Joined: 06/08/2020
Location: Canada
Posts: 290
Posted: 10:59am 30 Jan 2021
Copy link to clipboard 
Print this post

Thanks for the thoughtful replies.

I see merit in both top-down and bottom-up design approaches, and I imagine you always end up using a combination of both, as both techniques force you to deconstruct the problem and structure your solution in robust & elegant ways. "Spaghetti Zero" is always the goal :)

Peter M, if you are reading this, the mouse-enabled Editor is a joy to work with, and the page up / page down right-click gestures work extremely well.

Extending that concept, and pursuant to this modular coding discussion and the "Browse to Subroutine" capability mentioned by Neino, it would be awesome if:

* double-clicking on a sub or function or variable name jumped you to the next occurrence of that name
* a (modifier key + doubleclick) gesture jumped you to the previous occurrence
* a (modifier key + doubleclick) jumped you to the declaration of the name (or "first" / "last" occurrence, which would  be functionally equivalent)
* there would be keyboard-only versions of these  gestures

Hopefully you will consider something along these lines for the CMM2 roadmap.
Enjoy Every Sandwich / Joe P.
 
Print this page


To reply to this topic, you need to log in.

The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025