I'll leave this here for posterity...
The problem was not with the Vector method, but with the caller. I was casting the angle of roundAngle() to int instead of using the math::round function. D'oh!
This is supposed to constrict the vector to 45 degree slots. Only 2D, ignore the z component. The (rounding?) bug occurs when the correct answer is 180 and -90; this algorithm returns 179 and -89 respectively. Why?!
// ----------------------------------------------------
// round the angle to nearest x degrees
// ----------------------------------------------------
Vector3 Vector3::roundAngle ( int nearest_angle )
{
// vector to return
Vector3 rounded;
// convert to radians
double nearest_radians = RADIANS ( nearest_angle );
// get the angle of this vector
double angle = atan2 ( this->y, this->x );
// remainder between 2 angles
double remainder = std::fmod ( angle, nearest_radians );
// if there is a remainder, do the rounding
if ( remainder != 0 ) {
double new_angle = round ( angle / nearest_radians ) * nearest_radians;
rounded.x = cos ( new_angle );
rounded.y = sin ( new_angle );
}else{
rounded.x = this->x;
rounded.y = this->y;
rounded.z = this->z;
}
if ( fabs ( rounded.x ) < TOL ) rounded.x = 0;
if ( fabs ( rounded.y ) < TOL ) rounded.y = 0;
if ( fabs ( rounded.z ) < TOL ) rounded.z = 0;
return rounded.normalised();
}
Monday, 29 December 2014
Friday, 26 December 2014
RIP
The Amiga always had better graphics and sounds, but the first time I loaded a new season on PC, I knew its days were numbered!
Dad's IBM Thinkpad back in 1994. I installed Day of the Tentacle and Championship Manager 93 and I was hooked!
Thursday, 25 December 2014
Wednesday, 24 December 2014
Homepage
Here's a website for the game (if it's ever finished). Amazing what you can do with css these days.
Senseless Soccer Homepage
Senseless Soccer Homepage
Fixed Delta
Fixed as in "constant"... the engine now has a fixed time step (delta) for the physics simulations. Can you tell the difference?
Basically, the game step tries to run at 60 frames per second. The physics and rendering are decoupled and each can concede time back to one another. This means that the physics step may be executed more than once per frame, but always with the same time delta.
Basically, the game step tries to run at 60 frames per second. The physics and rendering are decoupled and each can concede time back to one another. This means that the physics step may be executed more than once per frame, but always with the same time delta.
Monday, 15 December 2014
Words of Wisdom
"Having one less character to type is NOT a good reason to prefer float over double..."
The extra precision can surprise you by fixing a lot of weird buggy stuff that you hadn't managed to trace back to floating point precision and, unless you're targeting some really specific old hardware or trying to simulate the universe or something, doubles don't cost you anything so might as well use em!
That's the problem with taking examples from badly written books - when your focus is on learning a bit of path-finding or something and the fact that the author riddled his code with floats just doesn't register!
Out of interest, switching to double fixed a few bugs like the rounding of vector angles to their nearest 45 degrees.
The extra precision can surprise you by fixing a lot of weird buggy stuff that you hadn't managed to trace back to floating point precision and, unless you're targeting some really specific old hardware or trying to simulate the universe or something, doubles don't cost you anything so might as well use em!
That's the problem with taking examples from badly written books - when your focus is on learning a bit of path-finding or something and the fact that the author riddled his code with floats just doesn't register!
Out of interest, switching to double fixed a few bugs like the rounding of vector angles to their nearest 45 degrees.
Saturday, 13 December 2014
Wednesday, 10 December 2014
Under the Hood
The past few late nights have been spent splitting the project into reusable components. It was already structured that way but now we have a shared game library that can be linked to for use in future games.
The lib can be linked statically or dynamically.
An example of the line between the game and the lib is the Player Sprite. The Player Sprite is part of the game and inherits from Sprite which is part of the generic library. Sprite then inherits from a generic Renderable object.
The lib also contains stuff like math tools (vectors), physics, input and Window management. Anything that could be reused for another game really!
P.S. Finally made the jump from bog-standard make to cmake for the shared library. It's just as cryptic but a little easier to use I guess!
The lib can be linked statically or dynamically.
An example of the line between the game and the lib is the Player Sprite. The Player Sprite is part of the game and inherits from Sprite which is part of the generic library. Sprite then inherits from a generic Renderable object.
The lib also contains stuff like math tools (vectors), physics, input and Window management. Anything that could be reused for another game really!
P.S. Finally made the jump from bog-standard make to cmake for the shared library. It's just as cryptic but a little easier to use I guess!
Thursday, 4 December 2014
Joystick Input
For future integration in-game, interface tweaked for joystick input (like SWOS).
Moving the joystick highlights different players or the ball (flashing border), pressing fire locks on to the currently selected object(fixed border) to be able to move it around. Pressing fire again unlocks and returns to the "select object" mode.
Moving the joystick highlights different players or the ball (flashing border), pressing fire locks on to the currently selected object(fixed border) to be able to move it around. Pressing fire again unlocks and returns to the "select object" mode.
Monday, 1 December 2014
Quick Tactics Editor in Java
Utilizing the simple grid system from the game, this makes a nice tool for visually setting up tactics and player positions for different situations.
Each player icon represents a player position structure, which is basically just an ordered list of numbers specifying which sector the player should try to get to for each ball sector. See this post for info.
Positions can be created, copied, imported and exported so a full tactic is simply a collection of 10 unique positions in a group.
In future, we'll add more detailed tactics and strategies like passing style, positions for set pieces, marking style etc.
Sunday, 30 November 2014
Invisible But Substantial!
Here's an invisible but quite substantial update. There are 2 improvements regarding the player sprite sheet graphic.
1. Instead of loading 20 (1 for each player) textures into memory, the player sprites now all share the same texture.
2. Instead of having the team colors in the image, thus requiring a whole new sprite sheet image for each different kit color, the sprite sheet is key coded and the colors are changed in-game via a palette system.
Next we need templates for different kit designs.
This will allow for changing/creating kits in game.
Implementation of the shared texture is rather trivial. We let the team own a shared texture object and simply point all player renderable textures to this.
For the color palette, we make a list of pairs indicating the old color (from png image) and the new color to replace it with. For example, replace all instances of RGB(255,0,0) with RGB(0,0,255). Add a pair for each of the shades of red, and this results in the above sprite sheet magically changing to a blue kit... w00t!
1. Instead of loading 20 (1 for each player) textures into memory, the player sprites now all share the same texture.
2. Instead of having the team colors in the image, thus requiring a whole new sprite sheet image for each different kit color, the sprite sheet is key coded and the colors are changed in-game via a palette system.
Next we need templates for different kit designs.
This will allow for changing/creating kits in game.
Implementation of the shared texture is rather trivial. We let the team own a shared texture object and simply point all player renderable textures to this.
offside! |
Saturday, 29 November 2014
Friday, 28 November 2014
Monday, 24 November 2014
Z-Order
A small but nifty enhancement: graphics are rendered in the correct order from the camera perspective. For example, here's the ball going "behind" the player instead of always on top.
Sunday, 23 November 2014
Saturday, 22 November 2014
Thursday, 20 November 2014
Player Values
This is an interesting one.
The Daily Mail reckons that, adjusted for inflation, an Alan Shearer in his prime would be worth 72 million squids today.
The sensi soccer algorithm valued him at 70.9 mil.
For Sheringham, DM says 28 million, sensi calculated 27.1 mil!
Ian Wright: 46mil vs 41.2mil.
Spooky!
The biggest discrepancy is the Mail valuing Robbie Fowler at 36 mil whilst sensi says 67.7 mil. Obviously sensi got it right - simply the greatest goalscorer of all time.
The Daily Mail reckons that, adjusted for inflation, an Alan Shearer in his prime would be worth 72 million squids today.
The sensi soccer algorithm valued him at 70.9 mil.
For Sheringham, DM says 28 million, sensi calculated 27.1 mil!
Ian Wright: 46mil vs 41.2mil.
Spooky!
The biggest discrepancy is the Mail valuing Robbie Fowler at 36 mil whilst sensi says 67.7 mil. Obviously sensi got it right - simply the greatest goalscorer of all time.
Wednesday, 19 November 2014
Monday, 17 November 2014
Saturday, 15 November 2014
Wrestlefest!
Monitor needs a black bezel and slight adjustment. Apart from that, not too bad at all!
Thursday, 13 November 2014
Wednesday, 12 November 2014
Building an Arcade Machine
Start with an old 4:3 monitor and Dell Optiplex for about $90 |
Case removed and programming the software! |
Arcade joystick, buttons and an I-pac keyboard encoder |
Speaker and amp |
Assembling the cabinet - step 1! |
Ikea?:p |
Getting there... |
Almost done! |
Now just need lifted up... |
Shell cab complete! |
View of the back side |
Wiring in the controls |
Microswitched buttons/joysticks (unwired) |
Wiring in the ipac keyboard encoder |
Controls done |
A sophisticated plan to mount the monitor :p |
Monitor mounted PC mounted to back door |
Designed in Gimp and printed at the local copyshop :p |
The finished article |
Monday, 10 November 2014
Sidetrack
Gave this a quick go on an impulsive whim - real time zooming feature. Worked out better than I thought! It might be interesting to play with for goal celebrations or replays... but tbh it was more a proof of concept for an idea I want to implement in my next project! ;)
Sunday, 9 November 2014
Small Update
- new pitch type
- tweaked ball physics
- goalkeeper diving progress
- perfect A.I. for current Liverpool back 4
- tweaked ball physics
- goalkeeper diving progress
- perfect A.I. for current Liverpool back 4
Friday, 17 October 2014
Monday, 13 October 2014
Another Sidetrack
In preparation for a home arcade machine, and since Hyperspin doesn't work on GNU/Linux, I've hacked together my own mame launcher:
Alternate theme for large game sets:
Alternate theme for large game sets:
Wednesday, 21 May 2014
Thursday, 15 May 2014
Thursday, 8 May 2014
And Finally
To conclude our little foray into h4X0R1ng, we might as well write a bit of code to output the data we've gathered so we go from this:
(yes, this is the Arsenal team in the original game format, with the hex editor translations on the right - big help, eh?)
to this:
Having already done the hard part to rip the data, the code for saving as plain text is trivial:
(yes, this is the Arsenal team in the original game format, with the hex editor translations on the right - big help, eh?)
to this:
Having already done the hard part to rip the data, the code for saving as plain text is trivial:
1: void write(std::vector< TEAM > team, const std::string& filename)
2: {
3: std::ofstream out_file;
4: out_file.open (filename.c_str());
5:
6:
7: out_file << "Team Format: [NAME] [COUNTRY CODE] [ID] [DIVISION] [MANAGER]"
8: << std::endl;
9: out_file << "Player Format: [NAME] [POSITION] [PASSING] [SHOOTING] [HEADING] [TACKLING] [BALL CONTROL] [SPEED] [FINISHING]"
10: << std::endl
11: << std::endl;
12:
13: for(unsigned int team_count=0; team_count LESS_THAN team.size(); team_count++){
14: out_file
15: << "------------------------" << std::endl
16:
17: << team[team_count].name
18: << " " << (int) team[team_count].id
19: << " " << (int) team[team_count].nation
20: << " " << (int) team[team_count].division
21: << " " << team[team_count].getCoachString().c_str() << std::endl
22:
23: << "------------------------" << std::endl
24:
25: << std::endl;
26:
27: for(unsigned int player_count=0; player_count LESS_THAN PLAYERS_PER_TEAM; player_count++){
28: out_file << team[team_count].players[player_count].name
29:
30: << " " << team[team_count].players[player_count].getPositionString()
31: << " " << (int) team[team_count].players[player_count].passing
32: << " " << (int) team[team_count].players[player_count].shooting
33: << " " << (int) team[team_count].players[player_count].heading
34: << " " << (int) team[team_count].players[player_count].tackling
35: << " " << (int) team[team_count].players[player_count].control
36: << " " << (int) team[team_count].players[player_count].speed
37: << " " << (int) team[team_count].players[player_count].finishing
38:
39: << std::endl;
40: }
41:
42: out_file << std::endl << std::endl;
43: }
44:
45: out_file.close();
46: }
Out of Interest
Here's how a non l33t h4X0R might knock up a quick program to get the data out of the sensi dat files.
It could be optimized, cleaned up, and generally written much better. But it gets the job done and that's all we care about for a one-shot program!
1: // number of players ineach team
2: const unsigned int PLAYERS_PER_TEAM = 16;
3: // number of characters in player name
4: const unsigned int PLAYER_NAME_LENGTH = 23;
5: // number of characters in team name
6: const unsigned int TEAM_NAME_SIZE = 19;
7: // number of characters in coach name
8: const unsigned int COACH_NAME_SIZE = 25;
9: struct PLAYER
10: {
11: SNIP!
12: };
13: struct KIT
14: {
15: SNIP!
16: };
17: struct TEAM
18: {
19: SNIP!
20: };
21: int main(int argc, char *argv[])
22: {
23: // must be 2
24: if ( argc != 2 )
25: {
26: std::cout << "ERROR:" << std::endl;
27: std::cout << "----------------------------" << std::endl;
28: std::cout << "usage: sensi_data <filename>" << std::endl;
29: std::cout << "----------------------------" << std::endl;
30: return 1;
31: }
32: // a list to store the teams
33: std::vector< TEAM > all_teams;
34: // open the teams data file as an input stream
35: std::ifstream input (argv[1], std::ios::in | std::ios::binary);
36: // team file data starts at byte 1
37: int byte_index = 1;
38: // stream input into this
39: unsigned char x;
40: // vector to store all bytes (loading them in to this first makes it easy to
41: // inspect the bytes in the debugger)
42: std::vector<unsigned char> bytes;
43: // tell the input stream NOT to skip anything (white spaces etc)
44: input >> std::noskipws;
45: // load the bytes into a vector
46: while (input >> x) {
47: bytes.push_back(x);
48: }
49: // loop controllers to parse all teams
50: int current_team = 1;
51: // the second byte in the file gives us the number of teams in the file
52: int number_teams = bytes[byte_index++];
53: // main loop for each team
54: while (current_team++ < number_teams){
55: // store the ripped data in a TEAM struct
56: TEAM team;
57: // unknown byte at start of each team (wtf?)
58: byte_index++;
59: team.nation = bytes[byte_index++]; // team country code
60: team.id = bytes[byte_index++]; // team id
61: team.id2 = bytes[byte_index++]; // unknown id
62: team.x1 = bytes[byte_index++]; // unknown byte
63: for(int i=0;i<TEAM_NAME_SIZE;i++){
64: team.name[i] = bytes[byte_index++]; // team name
65: }
66: team.tactics = bytes[byte_index++]; // tactics code
67: team.division = bytes[byte_index++]; // division
68: // -------------------------------------------
69: // KITS
70: // -------------------------------------------
71: team.kit1.type = bytes[byte_index++]; // kit 1 type
72: team.kit1.col1 = bytes[byte_index++]; // kit 1 main color
73: team.kit1.col2 = bytes[byte_index++]; // kit 1 secondary color
74: team.kit1.shorts= bytes[byte_index++]; // kit 1 shorts color
75: team.kit1.socks = bytes[byte_index++]; // kit 1 socks color
76: team.kit2.type = bytes[byte_index++]; // kit 2 type
77: team.kit2.col1 = bytes[byte_index++]; // kit 2 main color
78: team.kit2.col2 = bytes[byte_index++]; // kit 2 secondary color
79: team.kit2.shorts= bytes[byte_index++]; // kit 2 shorts color
80: team.kit2.socks = bytes[byte_index++]; // kit 2 socks color
81: // -------------------------------------------
82: // END KITS
83: // -------------------------------------------
84: for(int i=0;i<COACH_NAME_SIZE;i++){
85: team.coach_name[i] = bytes[byte_index++];// coach name
86: }
87: for(int i=0;i<15;i++){
88: team.x2[i] = bytes[byte_index++]; // 15 bytes of unknown data (what is this FOR?!)
89: }
90: // -------------------------------------------
91: // EACH PLAYER IN THE TEAM
92: // -------------------------------------------
93: for(unsigned int p=0;p<PLAYERS_PER_TEAM;p++){
94: team.players[p].nat = bytes[byte_index++]; // nationality
95: team.players[p].x1 = bytes[byte_index++]; // unknown byte
96: team.players[p].numb= bytes[byte_index++]; // shirt number
97: for(unsigned int i=0;i<PLAYER_NAME_LENGTH;i++){
98: team.players[p].name[i] = bytes[byte_index++]; // name
99: }
100: // ---------------------------------------------------------------
101: // PLAYER POSITION
102: //
103: // this byte stores the players position and skin/hair colour
104: // first 3 significant bits signify position as follows:
105: // 000 = goalkeeper
106: // 001 = right back
107: // 010 = left back
108: // 011 = defender
109: // 100 = right wing
110: // 101 = left wing
111: // 110 = midfielder
112: // 111 = attacker
113: //
114: // the next 2 bits signify skin/hair color:
115: // 00 = white skin/black hair
116: // 01 = white skin/blonde hair
117: // 10 = black skin/black hair
118: // 11 = unknown
119: team.players[p].position = (bytes[byte_index++] >> (5)) & 0xff;
120: //
121: // ---------------------------------------------------------------
122: team.players[p].x3 = bytes[byte_index++];// unknown byte
123: // ---------------------------------------------------------------
124: // player attributes.
125: // apart from passing, it's 2 attributes per byte
126: // 4 most significant bits and then 4 least significant bits
127: // for a total score out of 15 ( binary 1111) for each skill
128: // passing
129: team.players[p].passing = (bytes[byte_index++] ) & 0xf;
130: // shooting
131: team.players[p].shooting= (bytes[byte_index] >> (4)) & 0xff;
132: // heading
133: team.players[p].heading = (bytes[byte_index++] ) & 0xf;
134: // tackling
135: team.players[p].tackling= (bytes[byte_index] >> (4)) & 0xff;
136: // control
137: team.players[p].control = (bytes[byte_index++] ) & 0xf;
138: // speed
139: team.players[p].speed = (bytes[byte_index] >> (4)) & 0xff;
140: // finishing
141: team.players[p].finishing= (bytes[byte_index++] ) & 0xf;
142: //
143: // ---------------------------------------------------------------
144: team.players[p].value = bytes[byte_index++]; // value code
145: team.players[p].x4 = bytes[byte_index++]; // unknown byte
146: team.players[p].x5 = bytes[byte_index++]; // unknown byte
147: team.players[p].x6 = bytes[byte_index++]; // unknown byte
148: team.players[p].x7 = bytes[byte_index++]; // unknown byte
149: team.players[p].x8 = bytes[byte_index++]; // unknown byte
150: }
151: // -------------------------------------------
152: // END PLAYER
153: // -------------------------------------------
154: // save the team struct to the list
155: all_teams.push_back(team);
156: }
157: return 0;
158: }
Wednesday, 7 May 2014
A Little Dream Come True
Alright that's a bit of an over statement. But I remember back in the day, I must have been 15 years old max, with an interest in computers but absolutely no programming knowledge whatsoever. I almost wet my pants when I got an Amiga coverdisk with "Easy Amos" on it. It was supposed to let you make your own games. At that age, I couldn't get anywhere with it. What a disappointment.
It was a minor victory just to FIND the Sensible Soccer data files. What a disappointment then (after struggling just to OPEN the files) to be presented with something like this:
How the fuck was I supposed to update Liverpool with their exciting new signings, Sean Dundee and Bjorn Kvarme?! What a disappointment!
Fast forward a couple of years and the very beginnings of my education in computers. I was in the right circles, speaking to the right people. A HEX EDITOR, THAT'S WHAT THE FUCK I NEEDED!
Finally I'd be able to update the pool to their rightful glory with wonder signings such as Djimi Traore and Bernard Diomede!
So I cracked open my shiny new hex editor, loaded in the file, and...
FUUUUUUUUCK!!!!!!
Alright, at least there's something readable on the right, but this is just a dick tease! Maybe I could change a few names, have the beast Biscan playing for Liverpool, but he'd have all the stats of whoever he replaced. Ballix. And how can I change the stats? How can I give Heskey 10 out of 10 for "falling over"? How can I give Michael Owen -1 for loyalty? Robbie Fowler 4,000,000 for finishing? Where IS all this stuff ?!?! WHAT A DISAPPOINTMENT!
Anyway, I went on my merry way, learning my programming trade and only now, thanks to this Senseless Soccer project, have I rediscovered these dat files. Well guess what bitches. NOW I FINALLY have the knowhow to make those dat files sing to my own damn tune.
There is an online community keeping these data files up to date, which will be pretty cool for Senseless Soccer, but for now, JUST BECAUSE I CAN, here's some select tidbits ripped from the original SWOS amiga game: (attributes out of 15) - see next post for the code!
Note: As far as I know, the applied skill values in the game range from 1-7. A value over 7 signifies that this skill shows up as one of the 3 top skills for that player but in the actual gameplay, 7 is deducted to give it a val in the range 1-7.
So for example, Mark Wright has Passing, Heading and Tackling (PHT) as his 3 key skills but his passing rating of 12 is actually 5 out of 7.
The following screenshot is how this shows up in the game (couldn't find Liverpool team, sorry). Tony Adams has HTP as his key skills - heading, tackling and passing!
It was a minor victory just to FIND the Sensible Soccer data files. What a disappointment then (after struggling just to OPEN the files) to be presented with something like this:
How the fuck was I supposed to update Liverpool with their exciting new signings, Sean Dundee and Bjorn Kvarme?! What a disappointment!
Fast forward a couple of years and the very beginnings of my education in computers. I was in the right circles, speaking to the right people. A HEX EDITOR, THAT'S WHAT THE FUCK I NEEDED!
Finally I'd be able to update the pool to their rightful glory with wonder signings such as Djimi Traore and Bernard Diomede!
So I cracked open my shiny new hex editor, loaded in the file, and...
FUUUUUUUUCK!!!!!!
Alright, at least there's something readable on the right, but this is just a dick tease! Maybe I could change a few names, have the beast Biscan playing for Liverpool, but he'd have all the stats of whoever he replaced. Ballix. And how can I change the stats? How can I give Heskey 10 out of 10 for "falling over"? How can I give Michael Owen -1 for loyalty? Robbie Fowler 4,000,000 for finishing? Where IS all this stuff ?!?! WHAT A DISAPPOINTMENT!
Anyway, I went on my merry way, learning my programming trade and only now, thanks to this Senseless Soccer project, have I rediscovered these dat files. Well guess what bitches. NOW I FINALLY have the knowhow to make those dat files sing to my own damn tune.
There is an online community keeping these data files up to date, which will be pretty cool for Senseless Soccer, but for now, JUST BECAUSE I CAN, here's some select tidbits ripped from the original SWOS amiga game: (attributes out of 15) - see next post for the code!
Note: As far as I know, the applied skill values in the game range from 1-7. A value over 7 signifies that this skill shows up as one of the 3 top skills for that player but in the actual gameplay, 7 is deducted to give it a val in the range 1-7.
So for example, Mark Wright has Passing, Heading and Tackling (PHT) as his 3 key skills but his passing rating of 12 is actually 5 out of 7.
The following screenshot is how this shows up in the game (couldn't find Liverpool team, sorry). Tony Adams has HTP as his key skills - heading, tackling and passing!
Tuesday, 6 May 2014
A note on docs
If you are reasonably diligent in commenting your code using the doxygen markup convention, it can spit out some pretty meaningful and helpful documentation for you.
Here's an example of some documentation for the camera class. Everything is automatically generated, even the diagrams.
This gives you a good overview of the capabilities of the camera, and an indication of where you might want to go to make changes.
The diagram shows that the camera can follow a physical object (like a ball or a player). A physical object has the atrributes acceleration, position and velocity. The camera is itself a physical object!
It has rectangles to represent its viewport (visible area) and bounds (total world space).
The diagram also (correctly) indicates that the camera is a singleton class with an instance pointer to itself. I don't really like singletons so we'll be changing that. Besides, in this case a singleton isn't a good solution, since it's entirely logical to have more than one camera.
Pretty reasonable and informative!
Here's an example of some documentation for the camera class. Everything is automatically generated, even the diagrams.
This gives you a good overview of the capabilities of the camera, and an indication of where you might want to go to make changes.
The diagram shows that the camera can follow a physical object (like a ball or a player). A physical object has the atrributes acceleration, position and velocity. The camera is itself a physical object!
It has rectangles to represent its viewport (visible area) and bounds (total world space).
The diagram also (correctly) indicates that the camera is a singleton class with an instance pointer to itself. I don't really like singletons so we'll be changing that. Besides, in this case a singleton isn't a good solution, since it's entirely logical to have more than one camera.
Pretty reasonable and informative!
Sunday, 4 May 2014
Monday, 28 April 2014
Timeout
Getting in the mood for my next project... got myself an arcade fighting stick! (also works pretty well for sensi!)
Subscribe to:
Posts (Atom)