Java-Gaming.org    
Featured games (79)
games approved by the League of Dukes
Games in Showcase (477)
Games in Android Showcase (107)
games submitted by our members
Games in WIP (536)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Trade and Space Sim (aka Elite4k): Some questions  (Read 8261 times)
0 Members and 1 Guest are viewing this topic.
Offline BitDragon

Junior Member


Projects: 1


Sunset? Nice gradient paint!


« Posted 2009-01-17 13:38:01 »

Hi,

I've been working on Elite4k, a trade and space sim, since December. The game is basically a clone of evergreens like Elite and Privateer: You fly your spaceship from planet to planet, trade goods, buy upgrades, hunt pirates, mine asteroids or turn pirate yourself ... I think you all get the picture Smiley I've almost finished the game now, with a size of around 4.8k and enough optimization potential remaining  (hopefuly  Undecided) to reach 4k. However, some issues turned up during development which keep bothering me. I would be grateful for any input on the following questions:

Text rendering I'm currently using drawBytes to draw text from a resource byte array to a graphics context without string overhead. Is there a smarter way to do this? 

Number rendering I'm currently using a method which fills a byte array from a given number by setting the current modulo-10-value of the numeric value to the current byte, then decimal shifting the number and advancing the output byte. Is there a smarter way to do this? Perhaps some light-weight number format method in the API I don't know about?

Matrix inversion For the 3D space sim, the inverse camera matrix is required. Two ways to get it: Matrix inversion (my unrolled version is 12 lines with lots of multiplies and adds) or premultiplication with the negated matrix components. Which way to go? Premultiplication eliminates the inversion code but requires some control flow in the geometry pipeline. Any experiences?

Loading and Saving For a game where you can easily spend dozens of hours, a load and save game function is a must. The amount of game data I have to load and save is around 20 bytes. One option is to write out a text representation of the values and let the user note it down and reenter it to load a game (old-skool console style). However, 20 bytes mapped to easily typable characters is a lot of text. So I consider using the clipboard: Copy to get the current game state as a string, paste the string (which you saved in some file) to resume. Does this make sense? I'm a bit in doubt

Array Access When accessing array cells in approximately linear (depending on control flow) sequences, is it better to have something like "data[basepointer+1] (...) data[basepointer+2] (...)  data[basepointer+27] (...)" or is it better to go unary where possible "data[pointer++] (...) data[pointer++] (...)"?

Thanks for your input

Wolfgang

"I'm a ****ing starship, I'm allowed to cheat!"
GCU Arbitrary, Culture Craft




 
Offline moogie

JGO Knight


Medals: 12
Projects: 6
Exp: 10 years


Java games rock!


« Reply #1 - Posted 2009-01-17 14:14:05 »

For numbers perhaps this might be smaller?

g.drawBytes(Integer.toString(val).getBytes())

no idea tho...
Offline pjt33
« Reply #2 - Posted 2009-01-17 14:53:39 »

Loading and saving: using base 64, 20 bytes is 27 characters. That's not much longer than the passwords in some games I've played, and it's a bit more intuitive for a non-technical user than putting stuff in the system clipboard.

Array access: data[pointer++] repeated 27 times will compress a lot better than data[pointer + 1]...
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Abuse

JGO Coder


Medals: 11


falling into the abyss of reality


« Reply #3 - Posted 2009-01-17 15:01:42 »

Sounds very impressive!
Hope it lives up to expectations Cheesy

1) I'm pretty sure drawBytes is the cheapest way of drawing text
2) I think you're right again, the overhead of the necessary api method would be prohibitively expensive
3) sorry can't help there.
4) I'd bite the bullet & require the user to write down the code - I think you are going to be extremely strapped for bytes, making the usability hit an acceptable loss.
5) It depends.

It boils down to :-

1  
pointer++


1  
2  
iload x  // 1 or 2
iinc   x, 1 // 3 bytes


Vs

1  
pointer+1


1  
2  
3  
iload x // 1 or 2 bytes
iconst_y / bipush y / sipush y // 1/2/3 bytes (or more if a full int is needed)
iadd // 1 byte


Given the iload instruction used will be the same in both instances, they can be compared solely on the cost of the other instructions, giving a comparison of 3 bytes Vs 2+ bytes.

Ofcourse, this is all irrelevant, as 'pointer++' will undoubtably compress far better, so should always be used.

:edit:

it's worth noting that

1  
a[counter++] = 1;


Requires exactly the same bytecode (though in a slightly different order) as :-

1  
2  
a[counter] = 1;
counter+=1;


Which in turn is the same size as :-

1  
2  
a[counter] = 1;
counter+=2;


This is worth knowing, as people might expect 'pointer++' to generates smaller bytecode than 'pointer+=127'

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Offline Ranger
« Reply #4 - Posted 2009-01-17 17:04:18 »

Loading and Saving For a game where you can easily spend dozens of hours, a load and save game function is a must. The amount of game data I have to load and save is around 20 bytes. One option is to write out a text representation of the values and let the user note it down and reenter it to load a game (old-skool console style). However, 20 bytes mapped to easily typable characters is a lot of text. So I consider using the clipboard: Copy to get the current game state as a string, paste the string (which you saved in some file) to resume. Does this make sense? I'm a bit in doubt
This is what I found:
Clipboard: Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(""), null);   ==> adds 150 bytes
Dialog: JOptionPane.showInputDialog(this, null, "");  ==> adds 61 bytes
Offline Abuse

JGO Coder


Medals: 11


falling into the abyss of reality


« Reply #5 - Posted 2009-01-17 17:13:46 »

This is what I found:
Clipboard: Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(""), null);   ==> adds 150 bytes
Dialog: JOptionPane.showInputDialog(this, null, "");  ==> adds 61 bytes


It'd be smaller still if you used the variant without the 'parentComponent' parameter.

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Offline pjt33
« Reply #6 - Posted 2009-01-17 18:17:17 »

It'd be smaller still if you used the variant without the 'parentComponent' parameter.
Or even smaller if you just draw it to the screen, since OP is already using methods to draw text to the screen.
Offline BitDragon

Junior Member


Projects: 1


Sunset? Nice gradient paint!


« Reply #7 - Posted 2009-01-17 18:27:57 »

This is worth knowing, as people might expect 'pointer++' to generates smaller bytecode than 'pointer+=127'

Invaluable! Most of the rendering and game logic codes does little more than add and mul based on pointers. This will help a lot!

Thanks

Wolfgang

Offline BitDragon

Junior Member


Projects: 1


Sunset? Nice gradient paint!


« Reply #8 - Posted 2009-01-17 18:31:07 »

Or even smaller if you just draw it to the screen, since OP is already using methods to draw text to the screen.

Thanks for the input on the issue! Fortunately, neither the input nor the rendering of the save-game-string is the problem, I'll probably end up well below the mentioned byte counts by integrating it into the existing event logic.

From the responses so far, I conclude that a save-game-string of, say, 30, alphanumeric chars wouldnt be a problem (correct if wrong).

Thanks

Wolfgang
Offline BitDragon

Junior Member


Projects: 1


Sunset? Nice gradient paint!


« Reply #9 - Posted 2009-01-17 18:33:59 »

For numbers perhaps this might be smaller?

g.drawBytes(Integer.toString(val).getBytes())

no idea tho...


I tested this but (a) it compresses a lot worse than the solution I use now and (b) it does not support some required features. I need to draw tabular displays, with right-aligned numbers and a unit sign at the end of each number. Here is what I use now, input welcome:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
    /**
     * Formats a number into a byte array which can be rendered
     * as a string.
     *
     * @param numVal Numeric value to write
     * @param chrUni Sign to append to value
     * @returns Byte array of formatted numeric data
     */

    private byte[] f(int numVal, int chrUni)
    {
        // Define result
       byte[] resVal=new byte[8];
        // Write unit sign
       resVal[7]=(byte)chrUni;
        // Write values
       for(int digPos=6;digPos>=0;digPos--)
        {
            if(numVal>0)
                resVal[digPos]=(byte)(48+numVal%10);
            else
                resVal[digPos]=(byte)32;
            numVal/=10;
        }
        // Write zero
       if(resVal[6]==32) resVal[6]=48;
        // Return result
       return resVal;
    }


Thanks

Wolfgang
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline pjt33
« Reply #10 - Posted 2009-01-17 19:25:21 »

Without spending any time actually testing things, one probable-optimisation which jumps at me is moving the "Write zero" up before the "Write values" and skipping the test. I.e. replace
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
        // Write values
       for(int digPos=6;digPos>=0;digPos--)
        {
            if(numVal>0)
                resVal[digPos]=(byte)(48+numVal%10);
            else
                resVal[digPos]=(byte)32;
            numVal/=10;
        }
        // Write zero
       if(resVal[6]==32) resVal[6]=48;

with
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
        // Write zero
       resVal[6]=48;
        // Write values
       for(int digPos=6;digPos>=0;digPos--)
        {
            if(numVal>0)
                resVal[digPos]=(byte)(48+numVal%10);
            else
                resVal[digPos]=(byte)32;
            numVal/=10;
        }

That saves a few bytes in the uncompressed version.
Offline BitDragon

Junior Member


Projects: 1


Sunset? Nice gradient paint!


« Reply #11 - Posted 2009-01-17 19:30:37 »

Without spending any time actually testing things, one probable-optimisation which jumps at me is moving the "Write zero" up before the "Write values"
You are completely right. Will work and save a byte or two.

Thanks

Wolfgang

Offline Abuse

JGO Coder


Medals: 11


falling into the abyss of reality


« Reply #12 - Posted 2009-01-17 19:31:56 »

note - pjt33 replied while I was typing, so there maybe some duplication Wink

Only 2 optimisations I can see missing are to use the ternary operator & include the final if statement inside the loop.
Both of these eliminate the duplicate array access instructions, which are rather large.
Together. i'd guestimate around 10 byte saving?

Forgive the tampering with your literals - embedding non-trivial literals is one of my pet hates  Grin

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
    /**
     * Formats a number into a byte array which can be rendered
     * as a string.
     *
     * @param numVal Numeric value to write
     * @param chrUni Sign to append to value
     * @returns Byte array of formatted numeric data
     */

    private static byte[] f(int numVal, int chrUni)
    {
       // limit on the number of digits of numVal that will be displayed
      final int MAX_DIGITS = 7;
        // Define result
       byte[] resVal=new byte[MAX_DIGITS+1];
        // Write unit sign
       resVal[MAX_DIGITS]=(byte)chrUni;
        // Write values
       for(int digPos=MAX_DIGITS-1;digPos>=0;digPos--)
        {
            resVal[digPos] = (byte)(numVal>0||digPos==MAX_DIGITS-1?'0'+numVal%10:' ');
            numVal/=10;
        }
       // special case to handle numVal==0 moved into loop.
       
        return resVal;
    }


:edit:

Further optimised by combining the 2 ternary expressions inside the loop into a single or'ed one.

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Offline Abuse

JGO Coder


Medals: 11


falling into the abyss of reality


« Reply #13 - Posted 2009-01-17 19:34:37 »

You are completely right. Will work and save a byte or two.

Thanks

Wolfgang



erm, are you sure?

if numVal is passed as zero, it'll set resVal[6] to '0', and then immediately set it to ' '.
Breaking what you intended it to be doing.

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Offline BitDragon

Junior Member


Projects: 1


Sunset? Nice gradient paint!


« Reply #14 - Posted 2009-01-17 19:47:02 »


if numVal is passed as zero, it'll set resVal[6] to '0', and then immediately set it to ' '.
Breaking what you intended it to be doing.


Correct, and exiting early reintroduces the if. Stupid me. Thats what you get for debugging a geometry pipeline while playing solitaire with your wife and posting in boards simultaneously  Smiley

Anyhow, your optimizations have put an end to that discussion, very very nice!

Thanks

Wolfgang

PS: "Whoever finds embedded literals may keep them"  Grin
Offline Abuse

JGO Coder


Medals: 11


falling into the abyss of reality


« Reply #15 - Posted 2009-01-17 19:52:21 »

I'm looking forward to seeing this game! Grin

Faster! Faster! <cracks whip>

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Offline pjt33
« Reply #16 - Posted 2009-01-17 22:51:02 »

erm, are you sure?
Doh! There's an else clause.

Of course, if we can change the requirements we could have it pad with 0s rather than spaces for simpler, shorter code with a retro feel...
Offline moogie

JGO Knight


Medals: 12
Projects: 6
Exp: 10 years


Java games rock!


« Reply #17 - Posted 2009-01-17 22:57:45 »

I tested this but (a) it compresses a lot worse than the solution I use now and (b) it does not support some required features. I need to draw tabular displays, with right-aligned numbers and a unit sign at the end of each number.

Yeah, i wrote that at 2am... my brain was not really firing well.. it makes sense that it should compress worse!
Offline BitDragon

Junior Member


Projects: 1


Sunset? Nice gradient paint!


« Reply #18 - Posted 2009-01-18 09:26:57 »

Yeah, i wrote that at 2am... my brain was not really firing well.. it makes sense that it should compress worse!

Anyhow, your solution raises an issue I've been thinking about a lot: API calls versus do-it-yourself. As I see it, there are three basic cases. Two are no-brainers:

Never ever use the API, as in max/min or event handling, where the API calls compress much worse than your own constructs.
Always use the API, as in drawing ovals or filling polys, where your own constructs get much too long.

But the third is tricky: To use System.arrayCopy() or Arrays.fill(), or not? My guess is that for using such methods once, your have to pay the full overhead, but by using them frequently, the overhead should decrease ... where is the breakeven, when your own constructs start to get more expensive than the call overhead? I'm aware that this is almost a philosophical question and depends on the type of method we are talking about.

Offline pjt33
« Reply #19 - Posted 2009-01-18 13:50:47 »

Always use the API, as in drawing ovals or filling polys, where your own constructs get much too long.
I'm drawing my circles myself. Admittedly this is in large part because I'm doing complicated things with them (e.g. drawing an anti-aliased circle to the alpha channel).
Offline BitDragon

Junior Member


Projects: 1


Sunset? Nice gradient paint!


« Reply #20 - Posted 2009-01-18 15:19:15 »

I'm drawing my circles myself. Admittedly this is in large part because I'm doing complicated things with them (e.g. drawing an anti-aliased circle to the alpha channel).

Of course, my general statement does not hold if the drawing functionality is a central part of your app and involves complex stuff hard to do using the API. In Ares, I referenced the backbuffer as a byte array and implemented the fake-phong rasterizer based on that.

On the other hand: setRenderingHints, setComposite and drawOval is worse than a hand-rolled antialiasing bresenham? Obviously, I don't know any details ...
Offline Abuse

JGO Coder


Medals: 11


falling into the abyss of reality


« Reply #21 - Posted 2009-01-18 17:58:53 »

Of course, my general statement does not hold if the drawing functionality is a central part of your app and involves complex stuff hard to do using the API. In Ares, I referenced the backbuffer as a byte array and implemented the fake-phong rasterizer based on that.

On the other hand: setRenderingHints, setComposite and drawOval is worse than a hand-rolled antialiasing bresenham? Obviously, I don't know any details ...

When writing the algorithm yourself you can ofcourse sacrifice speed for code simplicity/size, so bresenham's algo. might not be the best choice.

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Offline pjt33
« Reply #22 - Posted 2009-01-18 18:11:30 »

Of course, my general statement does not hold if the drawing functionality is a central part of your app and involves complex stuff hard to do using the API. In Ares, I referenced the backbuffer as a byte array and implemented the fake-phong rasterizer based on that.

On the other hand: setRenderingHints, setComposite and drawOval is worse than a hand-rolled antialiasing bresenham? Obviously, I don't know any details ...
I hadn't come across setComposite before, so thanks for that. It doesn't do everything I need for Grav4k, but I may well find it useful in the future.

I'm doing fillOval , which is simpler than drawOval. I'm also, as Abuse suggested, sacrificing some speed for simplicity.
Offline BitDragon

Junior Member


Projects: 1


Sunset? Nice gradient paint!


« Reply #23 - Posted 2009-01-18 18:52:20 »

When writing the algorithm yourself you can ofcourse sacrifice speed for code simplicity/size

Comprehension dawns ... still thinking too much from a performance point of view. Of course you could get away with evaluating a distance function per pixel, especially if you use a lut, and for filling antialiasing would be cheap. Nice one, pjt33, and thanks for the clarification, abuse!
Offline rdcarvallo

Senior Member


Projects: 5
Exp: 15 years


2D Java games forever!


« Reply #24 - Posted 2009-02-01 06:25:48 »

A bit Off-topic,

    I found today Elite Plus for DOS boxed for $6.00. It's on two 5 1/4 floppies!! But 150 pages manual its pure game design gold.

   
Offline BitDragon

Junior Member


Projects: 1


Sunset? Nice gradient paint!


« Reply #25 - Posted 2009-02-02 09:29:21 »

A bit Off-topic,

    I found today Elite Plus for DOS boxed for $6.00. It's on two 5 1/4 floppies!! But 150 pages manual its pure game design gold.
  

5,25" floppies?!?! What did you do with those? Got a toaster with a USB port?  Grin Anyhow, the novel included in the manual alone is worth the price!

Still playing it on DOS-Box. By the way, the original Elite was about 22k in size ... so quite a challenge to wrestle it down to 4k, despite all the help the api provides.

Brief status update: Finished the second iteration last week and ended up at 5.5k compressed. Now coding the third iteration, with many new ideas ... there is hope  Smiley It looks like the space sim part (including models and all string resources for the trade sim) will end up at 3.2k, so at least a streamlined version of the trade sim should be possible.
Stuff I definitely had to drop: Station and docking (you just fly straight at the planet until you automatically land), Thargoids, multi-galaxy, tribbles  Grin.
Stuff that exceeds the original elite: Flat-Shaded graphics Tongue and the option to purchase a new ship (as a long-term motivation)
Galaxy size is 1024 systems. Boa, Mamba, Kraith, Viper and Cobra are included. So is asteroid mining, cargo pickup, refuelling at sun.

Nuff said, now I better go and live up to the promises
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

Riven (17 views)
2014-07-29 18:09:19

Riven (12 views)
2014-07-29 18:08:52

Dwinin (11 views)
2014-07-29 10:59:34

E.R. Fleming (29 views)
2014-07-29 03:07:13

E.R. Fleming (11 views)
2014-07-29 03:06:25

pw (40 views)
2014-07-24 01:59:36

Riven (39 views)
2014-07-23 21:16:32

Riven (27 views)
2014-07-23 21:07:15

Riven (29 views)
2014-07-23 20:56:16

ctomni231 (59 views)
2014-07-18 06:55:21
HotSpot Options
by dleskov
2014-07-08 03:59:08

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:58:24

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:47:22

How do I start Java Game Development?
by ra4king
2014-05-17 11:13:37

HotSpot Options
by Roquen
2014-05-15 09:59:54

HotSpot Options
by Roquen
2014-05-06 15:03:10

Escape Analysis
by Roquen
2014-04-29 22:16:43

Experimental Toys
by Roquen
2014-04-28 13:24:22
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!