English Amiga Board


Go Back   English Amiga Board > Coders > Coders. Language > Coders. Blitz Basic

 
 
Thread Tools
Old 24 April 2020, 13:17   #1
Havie
Registered User
 
Join Date: Mar 2012
Location: UK
Posts: 662
Printing Score with sprites (or blits)

Hi - so I am using sprites to display the score in my Flappy Bird game (could use blits).

Basically I am converting the score to a string and then slicing the string to get each digit and the converting the digit to it's value and adding this to my sprite image to get the correct number to display. Works fine.

Now that I am obsessed with efficiency and speed I am pretty certain there must be a better and faster way of getting the place value digit to use to add to the sprite image? Does anyone know a clever (and quick) mathematical way of doing this?

In my head you need to do this: say my number is 123

Divide the number by 100 giving 1.23 then INT the number which gives 1
so 1 is my first digit.

Then subtract 100*1 from 123 giving me 23.

The divide 23 by giving me 2.3 and INT (2.3) =2
so 2 is my second digit.

The subtract 10*2 from 23 giving me 3 which is my final digit.

This works but is it quicker? Are strings inherently slower than dividing?

BUT I still wonder if this can be done more efficiently at a binary level perhaps by rotating bits? I don't know 68000 machine code and it's along time since I did anything in Z80 so this is a bit beyond me at present. Anyone clever bods out there know a better method than the two I outlined?

As an aside, I always thought the INT command rounded the number but checked the manual and did a quick check and it doesn't - just chops off the decimal. There doesn't seem to be an easy way to round a decimal?

Last edited by Havie; 24 April 2020 at 13:58.
Havie is offline  
Old 24 April 2020, 14:43   #2
Daedalus
Registered User

Daedalus's Avatar
 
Join Date: Jun 2009
Location: Dublin, then Glasgow
Posts: 4,744
Multiplies and divides are very slow from a 68000 standpoint (though it improves as you go up the 68k range). Whether that's quicker than blitting the entire number, I don't know, but I suspect blitting 3 digits won't break the bank. Additionally, if you're working in decimals, you're involving quicks or floats which are again slower to deal with on a 68000 than bytes and words.

A game I worked on a while ago (and am still working on I guess...) blits scores every frame and updates them whenever necessary, and it's running within the limits of the blitter. So I'd say keep it simple. Keep a shape's bitmap and just print the string in one go when the score changes and blit the shape whenever you need it.
Daedalus is offline  
Old 24 April 2020, 23:42   #3
Havie
Registered User
 
Join Date: Mar 2012
Location: UK
Posts: 662
So strings is the way to go - although I'm not printing the string but using it to give me the correct image to print for each digit. After the authentic Flappy Bird font!

Thanks as always for your helpful advice!
Havie is offline  
Old 25 April 2020, 00:52   #4
jotd
This cat is no more
jotd's Avatar
 
Join Date: Dec 2004
Location: FRANCE
Age: 48
Posts: 4,178
A lot of games use binary coded decimal to avoid real time divisions. ABCD/SBCD then shift 4 by 4 to get the digits.

For instance if you have a score of 9546 represented it in hex: $9546. If you add 5 points use ABCD #5,<value> and you'll get $9551

start with score at 0 and do your additions using ABCD. Then move score in D0 and use and #F,d0 to get the digit, display and shift using LSR.L #4,d0, repeat nb digits times.
jotd is offline  
Old 25 April 2020, 00:55   #5
phx
Natteravn

phx's Avatar
 
Join Date: Nov 2009
Location: Herford / Germany
Posts: 1,667
Quote:
Originally Posted by Havie View Post
Are strings inherently slower than dividing?
You didn't mention how you create those strings. But assuming you are generating the string from a number then Basic has already done similar division and multiplication operations.

Quote:
BUT I still wonder if this can be done more efficiently at a binary level perhaps by rotating bits?
No. You can only do that with powers of two. On the 68000 you would use the divu instruction which gives you the integer result and the remainder of the operation at the same time. I am using BCD numbers for scores in my games, which can be converted without multiplications or divisions.

You might achieve a similar advantage when you store the single digits of your score, and write a simple addition function for it.

Quote:
As an aside, I always thought the INT command rounded the number but checked the manual and did a quick check and it doesn't - just chops off the decimal. There doesn't seem to be an easy way to round a decimal?
INT(x+0.5)
phx is offline  
Old 25 April 2020, 01:21   #6
Havie
Registered User
 
Join Date: Mar 2012
Location: UK
Posts: 662
Quote:
Originally Posted by phx View Post
INT(x+0.5)
Doh!
Havie is offline  
Old 25 April 2020, 01:22   #7
Havie
Registered User
 
Join Date: Mar 2012
Location: UK
Posts: 662
Quote:
Originally Posted by jotd View Post
A lot of games use binary coded decimal to avoid real time divisions. ABCD/SBCD then shift 4 by 4 to get the digits.

For instance if you have a score of 9546 represented it in hex: $9546. If you add 5 points use ABCD #5,<value> and you'll get $9551

start with score at 0 and do your additions using ABCD. Then move score in D0 and use and #F,d0 to get the digit, display and shift using LSR.L #4,d0, repeat nb digits times.
See- I knew that would be a clever 'shifty' way! Thanks.
Havie is offline  
Old 25 April 2020, 01:23   #8
Havie
Registered User
 
Join Date: Mar 2012
Location: UK
Posts: 662
But can I say - both answers blow my mind and will need some time assimilate...

But thanks to both of you.
Havie is offline  
Old 25 April 2020, 11:12   #9
roondar
Registered User

 
Join Date: Jul 2015
Location: The Netherlands
Posts: 1,965
The big question you should really ask yourself when discussing these kind of optimisations is (well IMHO anyway): how much of an impact does changing the algorithm really have on my game.

See, I love to try and find the fastest method for well anything. But I've found it's often counter productive to do so. You risk ending up spending ages optimising even the smallest aspect of your code, while not gaining much.

Case in point, let's ask some questions:
  • how much do you reckon will be saved per calculation by optimising this?
  • how often do you actually calculate the score? Once per second? Once per frame? Ten times per frame?
  • is your game running too slowly? Does it lose frames?
The answer to these questions (and a few similar ones) should tell you how much effort to put into optimising this particular problem. I'm willing to bet that calculating the score doesn't happen more than once per frame and I'm also willing to bet it doesn't need to be done once per frame at all to still be "good enough".

With that in mind, let me offer an two alternative ways of thinking about optimising the score display - neither of which remove divisions
  1. Don't optimise it at all, just leave it as is until you reach a point where the game won't run at 50Hz and you've looked at all things that do run more than once per frame already. The idea here being that you should always start optimising by looking at the most expensive/frequently execute code, not code that runs infrequently or has a small impact.
  2. Instead of calculating the entire score every frame, split the work across several frames. For instance, split it over three frames: one frame to calculate the first digit's object (and store the remainder), one frame to calculate the second digit (remainder) and one frame to calculate the third and last digit. This will be a bit more involved than the other options, but does show a key way of optimising things that don't have to run every frame to still be fine.

    If you really don't like the numbers updating on screen over three frames, you could either update the display during the third frame, or add a fourth frame where you deal with the display.
Food for thought
roondar is offline  
Old 25 April 2020, 11:30   #10
jotd
This cat is no more
jotd's Avatar
 
Join Date: Dec 2004
Location: FRANCE
Age: 48
Posts: 4,178
@roondar, then check Andrew Braybook games
jotd is offline  
Old 25 April 2020, 11:39   #11
roondar
Registered User

 
Join Date: Jul 2015
Location: The Netherlands
Posts: 1,965
Never checked his code, what did he do? Optimise everything to hell and back?
(this is a serious question by the way, I'm interested to know how he did it).
roondar is offline  
Old 25 April 2020, 12:02   #12
Havie
Registered User
 
Join Date: Mar 2012
Location: UK
Posts: 662
Quote:
The big question you should really ask yourself when discussing these kind of optimisations is (well IMHO anyway): how much of an impact does changing the algorithm really have on my game.
You are so right about this and we can all get to obsessed with maximum efficiency!

I did this with the tile map code - moved from drawing one column a frame to breaking it down to one tile a frame and it was a massive and complicated headache that took me about 4 days to get perfect. In terms of the game - essential to keep it within 1 frame but the score code - you're right, only needs to be update every time you pass a pipe so maybe once in 60 frames (or 30 if there are more pipes on screen). SO splitting it across 3 frames would be a waste of programming time unless I need to gain speed but in this instance all I am doing in each frame is:

- 1 block for the pipe piece
- 1 sprite for the bird
- 1 line of collision check code
- updating bird movement
- updating variable that keeps track of pipe (maximum of 6 loop)
- hardware scrolling the screen

In addition
- add 1 to score and display (every 30 frames minimum)

Which without testing will easily fit one frame.

The one thing I need to test that might be a problem is moving pipes. I plan to use blits to give the illusion of movement by blitting the bottom section+gap+top section on top of the pipe - around 60 pixelsx32 and I'm not sure if lots of blits this size (maybe up to 6 ) would be doable in 1 frame. Need to experiment before I try anything more complicated which is you point!

My original thought was to use sprites but I want the background to show through the gap (obviously) so the transparency wont work in this instance as it will show the pipe underneath that should be hidden by the gap...
Havie is offline  
Old 25 April 2020, 12:41   #13
jotd
This cat is no more
jotd's Avatar
 
Join Date: Dec 2004
Location: FRANCE
Age: 48
Posts: 4,178
Andrew (Fire & Ice, Uridium, Virocop) uses very compact assembly code, with bitfields, structures, does a lot of tricks to save a few cycles and all. for instance, when reading both joystick buttons, instead of testing bit 7 then bit 6, he does:

Code:
   move.b  $bfe001,d0
   bmi  .fire1notpressed
   btst  #6,d0
when loading in the register, it sets N if bit 7 is set (negative! and also fire not pressed). No need to test bit 7 then (but bit 6 needs testing). That's just a small example. If bits were 5 and 6 he couldn't have done that.

Also, in Fire and Ice, level numbers aren't 0,1,2,3,4 ... but 0,4,8,12,16... multiplying by 4 allows to use things like

Code:
   move.w level_number(pc),d0
   lea   some_table(pc),a0
   move.l   (a0,d0.l),d1
to get a pointer indexed on the level number without shifting D0 using LSL (or without using 68020 addressing modes (a0,d0.l*4))

saving cycles at each level is probably what makes good games fast, and bad games slow. I experienced that with Bagman remake I wrote in C++. I used the blitter and all, game has 4 planes and not a lot of objects, but the game can't run on A500 at a decent speed...

He also has a middleware/mini-operating system that he uses for his games. I prefer patching games coded by non-geniuses if you ask me
jotd is offline  
Old 25 April 2020, 13:20   #14
roondar
Registered User

 
Join Date: Jul 2015
Location: The Netherlands
Posts: 1,965
Quote:
Originally Posted by jotd View Post
Andrew (Fire & Ice, Uridium, Virocop) uses very compact assembly code, with bitfields, structures, does a lot of tricks to save a few cycles and all. for instance, when reading both joystick buttons, instead of testing bit 7 then bit 6, he does:
...
Also, in Fire and Ice, level numbers aren't 0,1,2,3,4 ... but 0,4,8,12,16... multiplying by 4 allows to use things like
...
Interesting stuff and exactly the kind of optimisation I was talking about. These things do save cycles. But so few of them that going out of your way to implement things this way is generally just not needed nor will it make any real difference to the end result.

Case in point: there are definitely Amiga games out there that run at 50Hz that do not use tricks like these, yet are still impressive.
Quote:
saving cycles at each level is probably what makes good games fast, and bad games slow. I experienced that with Bagman remake I wrote in C++. I used the blitter and all, game has 4 planes and not a lot of objects, but the game can't run on A500 at a decent speed...
I'm not fully disagreeing here, do optimise as needed (or if you really like to). But there is always a caveat: saving 10-20 cycles when reading the joystick like above will only save you a positively microscopic amount of performance. IMHO you should then only go to such effort if your code needs that level of effort to perform, otherwise it's just wasted time developing that could've been spend on other tasks.

It's much better to focus optimisation efforts on things that cost a lot of cycles or happen very often. That's where the real gains are. Saving 20 cycles per loop in a loop that runs a 30 times per frame is a lot more useful than saving 100 cycles for something that runs only once per frame. Same with expensive code, optimising a 4000 cycle routine to take 10% less cycles is a lot more useful than optimising a 200 cycle routine to take 50% less cycles.

So in the case of your Bagman remake it clearly was worthwhile to optimise a lot because it didn't run well. In the case of a game that already runs at 50Hz, it's just as clearly not worthwhile to spend a lot of time optimising (unless you expect to need to do a lot more down the line or are unhappy with the number of objects/effects/etc).
Quote:
He also has a middleware/mini-operating system that he uses for his games. I prefer patching games coded by non-geniuses if you ask me
Yeah, please don't take the above as criticism of his code, I'm merely trying to give some advice on how/when to optimise.

There is a time and place and I do advocate for writing efficient code, but focussing on eeking out that little bit of extra performance all the time is IMHO usually not worth it. Not only that, but I've found in my software developing career that it can lead to blinding you to much bigger improvements. What I mean here is optimising an existing routine to the hilt might stop you from considering thinking about an alternative approach because you're so laser-focussed on that one routine. While forgetting that a different approach might ultimately be a much better way of doing what you're trying to achieve faster.
roondar is offline  
Old 25 April 2020, 13:41   #15
jotd
This cat is no more
jotd's Avatar
 
Join Date: Dec 2004
Location: FRANCE
Age: 48
Posts: 4,178
Agreed! I never used WinUAE hotspot feature but it is supposed to show where the code is spending a lot of time. It doesn't "clock" the code but shows the memory areas where code execution is more frequent.

Of course nothing beats a real code profiler... on a modern platform.
jotd is offline  
Old 25 April 2020, 16:13   #16
Retro1234
Boo

Retro1234's Avatar
 
Join Date: Jun 2006
Location: 5150
Posts: 4,644
You have a good point there I quite often found with Blitz and Amos that you might spend quite a bit of time trying to get code to run at a good speed on 68000 but on 68020 it would be fast maybe even too fast, a big leap in speed etc

And maybe not possible in your game needs a separate score screen but use block command for the score etc and save sprites for something else.

Last edited by Retro1234; 25 April 2020 at 17:45.
Retro1234 is offline  
Old 28 April 2020, 15:05   #17
E-Penguin
Banana

E-Penguin's Avatar
 
Join Date: Jul 2016
Location: Darmstadt
Posts: 941
Quote:
Originally Posted by jotd View Post
A lot of games use binary coded decimal to avoid real time divisions. ABCD/SBCD then shift 4 by 4 to get the digits.

For instance if you have a score of 9546 represented it in hex: $9546. If you add 5 points use ABCD #5,<value> and you'll get $9551

start with score at 0 and do your additions using ABCD. Then move score in D0 and use and #F,d0 to get the digit, display and shift using LSR.L #4,d0, repeat nb digits times.
Be aware, ABCD is broken in BlitzBasic 2. It doesn't compile to the right opcode. Instead, use this macro:

https://eab.abime.net/showpost.php?p...1&postcount=27
E-Penguin is offline  
Old 28 April 2020, 23:30   #18
Havie
Registered User
 
Join Date: Mar 2012
Location: UK
Posts: 662
WIP in progress now in the zone.

Please feel free to let me know you thoughts...

http://eab.abime.net/zone/Flappy.adf
Havie is offline  
 


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools

Similar Threads
Thread Thread Starter Forum Replies Last Post
Only 4 ways to score in Sensible Soccer? twenty90seven support.Games 6 23 January 2015 02:19
Hard Blits under intuition & Gif To "struct BitMap" converter krabob Coders. Asm / Hardware 2 15 September 2014 17:25
Renaming Hi-Score Files cybernoid project.WHDLoad 14 01 July 2008 09:32
What's the biggest score you've ever seen? girv Retrogaming General Discussion 33 17 August 2007 23:50
Hi-score section Shatterhand project.EAB 6 29 March 2003 10:28

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +2. The time now is 05:24.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2020, vBulletin Solutions Inc.
Page generated in 0.08837 seconds with 13 queries