Author Topic: We're rewriting SMW  (Read 35557 times)

Offline EON8ight

  • Stomp Arena Staff
  • Regular
  • *****
  • Posts: 129
  • Dropped the screw in the tuna
    • EON8ight's Repository
We're rewriting SMW
« on: July 12, 2011, 12:33:53 PM »
I'm rewriting SMW in Java so I can support it myself (and possibly with a few Java-knowledgeable friends). It won't be an exact clone of the game - it'll be different, but the basic, fast-paced gameplay will remain the same. You guys can help me by telling me what you want from 1.8 to return in my version (and please don't say everything, as that's difficult considering this is my wide-scale programming endeavour). If anyone wants to help, send me a PM or something. I'm using Slick 2D game library to help write this, so if you have any experience with that and want to help, that would be great. Also, if any of you guys know how 1.8's map-saving function writes .map files, please let me know - I'm going to try to make my version compatible with all the maps currently compatible with 1.8.
« Last Edit: December 29, 2011, 02:50:53 PM by EON8ight »

Offline EON8ight

  • Stomp Arena Staff
  • Regular
  • *****
  • Posts: 129
  • Dropped the screw in the tuna
    • EON8ight's Repository
Re: I'm rewriting SMW
« Reply #1 on: July 13, 2011, 07:25:58 PM »
Blog to keep you guys updated.

Also, in the level editor, does anyone know what the purple skull and purple spikes and green skull tile types mean? I know they kill you somehow, but I know they also do something else.

Offline MSX

  • Posts Somewhat Often
  • *
  • Posts: 307
Re: I'm rewriting SMW
« Reply #2 on: July 18, 2011, 12:02:43 PM »
Quote from: "Blaspergerstoise"
Blog to keep you guys updated.

Also, in the level editor, does anyone know what the purple skull and purple spikes and green skull tile types mean? I know they kill you somehow, but I know they also do something else.

Super weird that nobody has responded to this post yet.
Only blame I find is vacation, but oh well.

Seriously, Blasper - If you just can someone put it together again, so we get solid gameplay - that should be it to begin with.
Best of luck, seriously =)
Crouching Tiger Hidden Spaghetti.

Offline JM Dragon

  • Stomp Arena Staff
  • this title came sooner than the last
  • **
  • Posts: 3629
Re: I'm rewriting SMW
« Reply #3 on: July 18, 2011, 09:49:47 PM »
Interesting. I've thought about rebooting SMW as well, but I don't have much time, due to work. But, I can probably contribute as a minor programmer (I need the experience anyway).
 

Offline EON8ight

  • Stomp Arena Staff
  • Regular
  • *****
  • Posts: 129
  • Dropped the screw in the tuna
    • EON8ight's Repository
Re: I'm rewriting SMW
« Reply #4 on: October 06, 2011, 07:47:50 PM »
I haven't updated in a long time, so I thought I'd tell you guys this: I am still working on the project.

I've spent the last few months in college-application hell, plus the past two weeks I've been recovering from wisdom tooth surgery (which impacted a lot of stuff I was working on at the time). I'm still applying to colleges, since I chose an ungodly number of 19 colleges to apply to. I've finished about half, and I plan on knocking a few more out this weekend.

About the game: I am going to finish this no matter what. I'm very determined to produce a game. I'm taking a second-level programming class right now, and this class (and the one I took last year) have helped a lot. I would not have started this project if I weren't confident in my abilities, so I promise you guys I will have something. Maybe not soon, but I can guarantee you'll be playing it before I go off to college next year.

I just finished writing the beginning of the collision engine. I had no idea where to start, so I spent most of my English class writing notes about how I would get it to work. Right now, it's all based on a 2D array of boolean values. Each value represents a pixel on the screen, and if that value is true, then it's solid. If not, it's "empty." A loop goes through the 32 pixels directly below the player, and if any one of them is true, then the player is determined to be standing on solid ground. Otherwise, the player will continue to fall. I didn't expect it to work, but after only half a dozen lines of code, guess what? It actually works. The player actually makes contact with the ground and doesn't fall.

Also, no more engines. I'm writing this thing entirely with no external JARs. The engine is one I'm writing entirely by myself.

It's coming along nicely, I'd say.

Here's the Player class; the object the player controls, and the parent class of the AI class.

Code: [Select]
package eon8ight.dmgame.Entity;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Hashtable;
import javax.imageio.ImageIO;
import eon8ight.dmgame.*;

public class Player implements KeyListener
{
/*
* Hashtable "sprites" contains the following sprites:
*
* stand
* walk
* jump
* turn
* loselife
* dead
* stand_left
* walk_left
* jump_left
* turn_left
*/
public Hashtable<String, BufferedImage> sprites;
public Animation walkingRight;
public Animation walkingLeft;
public BufferedImage currentImage;

public volatile float x;
public volatile float y;

/*
* boolean[] "controls" has the following definitions:
* [0] = UP
* [1] = DOWN
* [2] = LEFT
* [3] = RIGHT
*/
private boolean[] controls;
private boolean facingRight;
private boolean onGround;

public Player(String skinName) throws IOException
{
//Step 1: Set the skin.
File skinFile = new File("res/gfx/skins/" + skinName + ".png");
File defaultSkin = new File("res/gfx/skins/default.png");

BufferedImage skin = (skinFile.exists()) ? ImageIO.read(skinFile) : ImageIO.read(defaultSkin);

//Step 2: Create the different sprite states.
sprites = new Hashtable<String, BufferedImage>();
BufferedImage stand = skin.getSubimage(0, 0, 32, 32);
BufferedImage standLeft = Sprite.getHorizontalFlippedCopy(stand);
BufferedImage walk = skin.getSubimage(32, 0, 32, 32);
BufferedImage walkLeft = Sprite.getHorizontalFlippedCopy(walk);
BufferedImage jump = skin.getSubimage(64, 0, 32, 32);
BufferedImage jumpLeft = Sprite.getHorizontalFlippedCopy(jump);
BufferedImage turn = skin.getSubimage(96, 0, 32, 32);
BufferedImage turnLeft = Sprite.getHorizontalFlippedCopy(turn);
BufferedImage loseLife = skin.getSubimage(128, 0, 32, 32);
BufferedImage dead = skin.getSubimage(160, 0, 32, 32);

//Step 3: Add the states to the Hashtable.
sprites.put("stand", stand);
sprites.put("walk", walk);
sprites.put("jump", jump);
sprites.put("turn", turn);
sprites.put("loselife", loseLife);
sprites.put("dead", dead);
sprites.put("stand_left", standLeft);
sprites.put("walk_left", walkLeft);
sprites.put("jump_left", jumpLeft);
sprites.put("turn_left", turnLeft);

//Step 4: Create the animation.
try
{
walkingRight = new Animation(80, walk, stand);
walkingLeft = new Animation(80, walkLeft, standLeft);
}
catch(EONException e)
{
e.printStackTrace();
}

//Step 5: Set the variables.
currentImage = stand; //later on, when gravity is implemented, this will be "jump"

x = 100; //(float)Math.random() * 1024;
y = 0; //(float)Math.random() * 768;

controls = new boolean[4];

facingRight = true;
onGround = false;
}

@Override
public void keyPressed(KeyEvent e)
{
//Step 1: Get key code.
int keyCode = e.getKeyCode();

//Step 2: Decide what to do based on key code.
if(keyCode == KeyEvent.VK_RIGHT)
{
facingRight = true;
controls[3] = true;
currentImage = (BufferedImage)walkingRight.getImage();
}

if(keyCode == KeyEvent.VK_LEFT)
{
facingRight = false;
controls[2] = true;
currentImage = (BufferedImage)walkingLeft.getImage();
}

if(keyCode == KeyEvent.VK_DOWN)
{
controls[1] = true;
currentImage = facingRight ? sprites.get("stand") : sprites.get("stand_left");
}

if(keyCode == KeyEvent.VK_UP)
{
controls[0] = true;
currentImage = sprites.get("jump");
}

e.consume();
}

@Override
public void keyReleased(KeyEvent e)
{
//Step 1: Get key code.
int keyCode = e.getKeyCode();

//Step 2: Turn key code's corresponding boolean off.
if(keyCode == KeyEvent.VK_RIGHT)
controls[3] = false;

if(keyCode == KeyEvent.VK_LEFT)
controls[2] = false;

if(keyCode == KeyEvent.VK_DOWN)
controls[1] = false;

if(keyCode == KeyEvent.VK_UP)
controls[0] = false;

//Step 3: Set the sprite.
currentImage = facingRight ? sprites.get("stand") : sprites.get("stand_left");

e.consume();
}

@Override
public void keyTyped(KeyEvent e)
{
e.consume();
}

public synchronized void update(long timePassed)
{
if(controls[2] || controls[3])
{
if(facingRight)
{
x += Physics.VELOCITY_X * timePassed;
walkingRight.update(timePassed);
}
else
{
x -= Physics.VELOCITY_X * timePassed;
walkingLeft.update(timePassed);
}
}
if(controls[0])
y -= Physics.VELOCITY_Y * timePassed;

else if(!onGround)
y += Physics.VELOCITY_Y * timePassed;
}

public synchronized void collisionLogic(Test world)
{
onGround = false;

for(int i = Math.round(x); i < Math.round(x) + 32; i++)
{
if(world.pixelSpots[Math.round(x)][Math.round(y)])
{
onGround = true;
break;
}
}
}
}

Offline MSX

  • Posts Somewhat Often
  • *
  • Posts: 307
Re: I'm rewriting SMW
« Reply #5 on: October 08, 2011, 07:15:33 AM »
Awesome we can't wait!!
Crouching Tiger Hidden Spaghetti.

Offline Rockerfrick

  • Lots of Free Time
  • ****
  • Posts: 1062
  • Theory of Nekomata
    • Nihilist Philanthropy
Re: I'm rewriting SMW
« Reply #6 on: October 09, 2011, 09:34:20 AM »
Converted a portion of the whole code to Java? That sounds interesting.
[smwstuff]914[/smwstuff]

New Jawbox | Map Request Thread | Old Jawbox

Thou shalt not pie if thou shalt not meal.


Offline Felix-The-Ghost

  • Too Much Free Time
  • *****
  • Posts: 1306
  • 1v1 Rust
    • http://members.allegro.cc/felix-the-ghost/index.html
Re: I'm rewriting SMW
« Reply #7 on: October 09, 2011, 01:43:52 PM »
Quote
Right now, it's all based on a 2D array of boolean values. Each value represents a pixel on the screen, and if that value is true, then it's solid. If not, it's "empty." A loop goes through the 32 pixels directly below the player, and if any one of them is true, then the player is determined to be standing on solid ground.

That sounds kinda gross...

I'd recommend a 2D array of integers, for tiles, not pixels, a number for each tile type, since there are more than just two types.

Each tile is 32pixels wide (I think)

So to figure out which one the player is standing under,

Divide the players x/y coordinates by 32 (or the tile-width) and you'll get the index in the array for the tile. Add 1 to the y index for the tile below the player.

ie.

  • [0][0][0][0][0][0][0][0]
  • [0][0][0][0][0]
[M][0][0][0]
  • [0][1][0][1][1][1][0][0]
  • [0][0][0][0][0][0][0][0][0]

    0=Air
    1=solid tile
    M = Mario

    We'll just pretend for now the field is a mere 9 x 4 tiles :)
    Each tile is 32px x 32px.
    We'll say the player is located at X200, Y64.
    Dividing X and Y by the tile width with rounding gets us X6, Y2.
    Add 1 to the Y to get the tile below the player, and we get X6, Y3.
    That's solid! (1) so the player is standing on solid ground.

    You'll probably have to use bottom-middle point of the player when checking collision instead of one side.

    If you want pixel-perfect collision, do this first to isolate the tile to compare so your not looping through the opposite side of the map to determine if the player has collided with it ;)

Quote
Also, no more engines. I'm writing this thing entirely with no external JARs.
:X

Quote
Also, in the level editor, does anyone know what the purple skull and purple spikes and green skull tile types mean? I know they kill you somehow, but I know they also do something else.

???

Offline EON8ight

  • Stomp Arena Staff
  • Regular
  • *****
  • Posts: 129
  • Dropped the screw in the tuna
    • EON8ight's Repository
Re: I'm rewriting SMW
« Reply #8 on: October 09, 2011, 04:21:29 PM »
Quote from: "Felix-The-Ghost"
[text]
I've changed the way collision detection works based on Big JM's advice.

Everything is now Rectangles, and the collision is handled by intersects(Rectangle r). The exact method that contains collision detection is:

Code: [Select]
public synchronized void collisionLogic(MapScene world)
{
//Step 1: Make assumptions.
onGround = false;
canMoveLeft = true;
canMoveRight = true;
canMoveUp = true;

//Step 2: Get relative X and Y.
int relX = Math.round(x / 32);
int relY = Math.round(y / 32);

if(relX - 1 < 0 || relX + 1 > 31 || relY - 1 < 0 || relY + 1 > 23)
return;

//Step 3: Check if the player is on the ground.
if(world.tiles[relX][relY + 1] != null)
{
if(this.intersects(world.tiles[relX][relY + 1]))
{
onGround = true;
y = relY * 32;
}
}

//Step 4: Check if the player is about to move through a wall.
if(controls[2])
{
if(world.tiles[relX - 1][relY] != null)
{
if(this.intersects(world.tiles[relX - 1][relY]))
{
canMoveLeft = false;
x = relX * 32;
}
}
}
else if(controls[3])
{
if(world.tiles[relX + 1][relY] != null)
{
if(this.intersects(world.tiles[relX + 1][relY]))
{
canMoveRight = false;
x = relX * 32;
}
}
}

//Step 5: Check if the player is about to jump through a tile.
//this also has the "magnet effect" problem
if(controls[0])
{
if(world.tiles[relX][relY - 1] != null)
{
if(this.intersects(world.tiles[relX][relY - 1]))
{
canMoveUp = false;
y = relY * 32;
}
}
}
}

Player extends abstract class Entity, and Entity extends Rectangle.

Basically, the game checks if the player is colliding with a tile at the player's X / 32 and the player's Y / 32. If the player intersects, the collision is made pixel-perfect by dividing the player's Y or X by 32, rounding the result, and multiplying the result of the rounding by 32.

Here's some relevant bits from the MapScene class:

Code: [Select]
//writing collision by hand is awful
private void setupDevMap()
{
tiles = new Tile[32][24];

for(int i = 1; i <= 7; i++)
tiles[i][8] = block;

tiles[1][6] = block;
tiles[1][5] = block;
tiles[2][5] = block;
tiles[3][5] = block;
tiles[1][7] = block;
tiles[7][7] = block;
}

public void update(long timePassed)
{
[...other stuff cut out]
player.collisionLogic(this);
}

public void draw(Graphics2D g)
{
[...other stuff cut out]
//y u so off by one on the leftmost side?
for(int i = 0; i < tiles.length; i++)
{
for(int j = 0; j < tiles[i].length; j++)
{
if(tiles[i][j] != null)
g.drawImage(block.tile, TILE_WIDTH * i, TILE_WIDTH * j, null);
}
}
}

Tile extends Rectangle and has a variable called "tile," a BufferedImage.

There's a problem I'm running into with this one: the collision is broken on the leftmost block in a series of blocks. In other words, there can be a tile with collision, but there must also be an "invisible tile" with collision directly left of the block for collision to appear correct. For example, this is what I expect when the blocks are drawn after collision data is set:

Code: [Select]
[X][X][X][X][X][X][X][X]
[ ][ ][ ][ ][ ][ ][ ][ ]

(imagine the X blocks (tiles) and the empty blocks (collision) are on the same level)

But for some reason, the player is never able to collide with the leftmost block, leaving the actual, collisive map looking like this:

Code: [Select]
[X][X][X][X][X][X][X][X]
   [ ][ ][ ][ ][ ][ ][ ]

The two problems I mentioned are hard to describe with words, so if you want to see what they look like, I can give you a copy of the necessary source code.

EDIT: I fixed the magnet effect when the player collides with an object while moving left. I changed relY from Math.round(y) / 32 to Math.round(y) * 32. However, this completely destroyed the player's ability to jump when on top of a tile (as in, the player can no longer move in an upward direction if it is standing on top of a tile). I'm working on a solution to this.

Offline MSX

  • Posts Somewhat Often
  • *
  • Posts: 307
Re: I'm rewriting SMW
« Reply #9 on: October 09, 2011, 07:47:04 PM »
Keep updating us!!

If you need any help let me know (Tileset-wise)
Crouching Tiger Hidden Spaghetti.

Offline Felix-The-Ghost

  • Too Much Free Time
  • *****
  • Posts: 1306
  • 1v1 Rust
    • http://members.allegro.cc/felix-the-ghost/index.html
Re: I'm rewriting SMW
« Reply #10 on: October 10, 2011, 01:55:43 AM »
Quote
I've changed the way collision detection works based on Big JM's advice.
Is he PM'ing you? Looks like some of that is also my advice.

Quote
Everything is now Rectangles, and the collision is handled by intersects(Rectangle r). The exact method that contains collision detection is:

I assume you are referring to bounding box collision.

Quote
[X][X][X][X][X][X][X][X]
[ ][ ][ ][ ][ ][ ][ ][ ]

Sounds like the rounding is failing to return anything below 1 in the X index. Perhaps the Y index has the same collision problem.

For debugging, try printing variables on the screen.

Quote
I fixed the magnet effect when the player collides with an object while moving left. I changed rely from Math.round(y) / 32 to Math.round(y) * 32.

That's weird. Why would the Y rounding interfere with the X placement?

Quote
Code: [Select]
if(relX - 1 < 0 || relX + 1 > 31 || relY - 1 < 0 || relY + 1 > 23)
      return;

I see this is checking to see if the player is at the bounds of the map, but what exactly is it returning if that statement is true?

Quote
Code: [Select]
   for(int i = 0; i < tiles.length; i++)
   {
      for(int j = 0; j < tiles[i].length; j++)

It looks like this is supposed to be looping through a 2D array for x and y, but it'd not really formatted like that. I don't really know what you're accessing here.

Offline EON8ight

  • Stomp Arena Staff
  • Regular
  • *****
  • Posts: 129
  • Dropped the screw in the tuna
    • EON8ight's Repository
Re: I'm rewriting SMW
« Reply #11 on: October 10, 2011, 11:21:04 AM »
Quote from: "Felix-The-Ghost"
Quote
I fixed the magnet effect when the player collides with an object while moving left. I changed rely from Math.round(y) / 32 to Math.round(y) * 32.

That's weird. Why would the Y rounding interfere with the X placement?
I meant x, not y. So that's Math.round(x) * 32.

Quote from: "Felix-The-Ghost"
Quote
Code: [Select]
if(relX - 1 < 0 || relX + 1 > 31 || relY - 1 < 0 || relY + 1 > 23)
      return;

I see this is checking to see if the player is at the bounds of the map, but what exactly is it returning if that statement is true?
What that'll do is it'll skip the assumption-checking and assume all the assumptions made in step 1 are true. So in other words, it'll assume the player is not on the ground and not about to collide with a tile without actually checking if those assumptions are true.

Quote from: "Felix-The-Ghost"
Quote
Code: [Select]
   for(int i = 0; i < tiles.length; i++)
   {
      for(int j = 0; j < tiles[i].length; j++)

It looks like this is supposed to be looping through a 2D array for x and y, but it'd not really formatted like that. I don't really know what you're accessing here.
It's an extremely inefficient loop that draws tiles on the screen. If the tile at [j] is not null, then draw that tile's image.

Offline JM Dragon

  • Stomp Arena Staff
  • this title came sooner than the last
  • **
  • Posts: 3629
Re: I'm rewriting SMW
« Reply #12 on: October 10, 2011, 01:43:51 PM »
Yeah, we were talking over PM. I'm still thinking on how to fix that collision issue.
 

Offline EON8ight

  • Stomp Arena Staff
  • Regular
  • *****
  • Posts: 129
  • Dropped the screw in the tuna
    • EON8ight's Repository
Re: I'm rewriting SMW
« Reply #13 on: October 10, 2011, 03:12:46 PM »
Good news: I've fixed the jumping problem simply by checking if the player is jumping before checking if the player is on the ground. There's also no magnet effect either. So all that needs to be fixed is that off-by-one-on-the-left-side collision problem.

Also, something weird: collision is now broken on both sides. On the leftmost side, the player will fall through the block if it is on the leftmost half of the block. On the rightmost side, the player will fall through the block if it is on the rightmost half of the block.

Offline Felix-The-Ghost

  • Too Much Free Time
  • *****
  • Posts: 1306
  • 1v1 Rust
    • http://members.allegro.cc/felix-the-ghost/index.html
Re: I'm rewriting SMW
« Reply #14 on: October 10, 2011, 06:20:54 PM »
Quote
 
Code: [Select]
for(int i = 0; i < tiles.length; i++)
   {
      for(int j = 0; j < tiles[i].length; j++)

Quote
It's an extremely inefficient loop that draws tiles on the screen. If the tile at
Code: [Select]
[i][j]
is not null, then draw that tile's image.

I don't really know what you're trying to do, I don't know why you'd ever compare
Code: [Select]
j < tiles[i].length
and  
Code: [Select]
i < tiles.length
, they seem unrelated.

I'd expect something like
Code: [Select]
for(int i = 0; i < tiles[i].length; i++)
   {
      for(int j = 0; j < tiles[j].length; j++)

Notice the changes, that's what I would've thought you intended to write.


Quote
Also, something weird: collision is now broken on both sides. On the leftmost side, the player will fall through the block if it is on the leftmost half of the block. On the rightmost side, the player will fall through the block if it is on the rightmost half of the block.

That's obviously a rounding issue. Use the relative X/Y approach to find out what general tiles to check, [s:3bjqs26r]then you will have to do a bounding box check.[/s:3bjqs26r] Test a tile/collision map with gaps in it, and see if that works, if I'm right you'll fall through the edges of those too, currently.

Dang it. I always used HTML on BBCode forums and BBCode on HTML forums >:(
crap. the freaking [ i ] array is screwing with the BBCode.

Hey Big JM, any chance of inline code? Not instead of, but to use for code with like one line? So it doesn't have that huge block for one line of code?
« Last Edit: October 10, 2011, 06:33:49 PM by Felix-The-Ghost »