Monday, July 25, 2011

Platform Jumping needs vector-ing

I was, on my way to showing a simple jump mechanic for a platformer when things just got way to complex for my liking especially the different physics variables, gravity, player speeds, jumping states etc... I had a speed constant and an velocity for jumping and for gravity and for the player. I had this messed up sense of the direction the player was moving, which was a radian angle. It didn't lend itself well to player movement in a platformer. I was flip flopping sin/cos and rotating the angles all over the place. It was a mess, then I just gave in and started using vectors.

Without vectors I had the player falling through the platforms even when it detected hits. I had a bug where I could only move in the air, then I was able to only move on the platform. Had the player repeatedly flying straight up like a rocket. And some double jump code didn't work right either, I kept increasing the velocity applied for the jumping to the point where you wouldn't just smoothly jump up. You'd immediately reach your apex and slowly come back down due to gravity. So yeah there is a lot of stuff to figure out.

So here is the vector class in all it's wondrous glory, I've used it to good effect so far without needing many changes. The most important functions are the normalize, magnitude, dot, and cross product functions. Those are necessary to perform many physics calculations.

Caveats: Originally I made this an immutable type but later added mutation methods (set(x,y)). There's probably a few hundred ways to do these calculations, for instance you could just use stand-a-lone static functions, or maybe a class that uses all Point2D.Double's for the vector data, etc... The ZERO static is not usable in calculations if you use the mutator methods, it will mutate the global instance. (oops!!! yeah I found it out because I was using it, should make a subclass for ZERO as a completely immutable type.)

/**
 * File: Vector.java
 *
 * This file is free to use and modify as it is for educational use.
 * brought to you by Game Programming Snippets (http://gpsnippets.blogspot.com/)
 *
 * Revisions:
 * 1.1 Initial Revision
 *
 */
package snippets.math;

/**
 * Mathematical vector class implementation. Provides the necessary methods to
 * use vector in physics calculations, ray tracing operations, and other types
 * of directional vectoring math applications. 
 *
 * @author Nick
 */
public class Vector {

 public static Vector ZERO = new Vector(0, 0);

 private double x = 0.0d;
 private double y = 0.0d;

 public Vector(double x, double y) {
  this.x = x;
  this.y = y;
 }

 public Vector add( double c ) {
  return new Vector(this.x+c,this.y+c);
 }

 public Vector add( double x, double y ) {
  return new Vector(this.x+x,this.y+y);
 }

 public Vector add( Vector v ) {
  return new Vector(this.x+v.x,this.y+v.y);
 }

 public Vector cross( Vector v ) {
  return new Vector(this.y - v.y, this.x - v.x );
 }

 public Vector divide( double c ) {
  return new Vector(this.x/c,this.y/c);
 }

 public Vector divide( double x, double y ) {
  return new Vector(this.x/x,this.y/y);
 }

 public Vector divide( Vector v ) {
  return new Vector(this.x/v.x,this.y/v.y);
 }

 public double dot( Vector v ) {
  return this.x * v.x  + this.y * v.y;
 }

 @Override
 public boolean equals(Object o) {
  if(o instanceof Vector) {
   Vector v = (Vector)o;
   return x == v.x && y == v.y;
  }
  return false;
 }

 public double getX() {
  return x;
 }

 public double getY() {
  return y;
 }

 public double magnitude() {
  return Math.sqrt(this.x * this.x + this.y * this.y);
 }

 public Vector multiply( double c ) {
  return new Vector(this.x*c,this.y*c);
 }

 public Vector multiply( double x, double y ) {
  return new Vector(this.x*x,this.y*y);
 }

 public Vector multiply( Vector v ) {
  return new Vector(this.x*v.x,this.y*v.y);
 }

 public Vector negative() {
  return this.multiply(-1);
 }

 public Vector normalize() {
  double mag = magnitude();
  if(mag != 0) {
   return new Vector(x/mag, y/mag);
  }else {
   return new Vector(x,y);
  }
 }

 public void set(int i, int j) {
  this.x = i;
  this.y = j;
 }
 
 public void set(double x, double y) {
  this.x = x;
  this.y = y;
 }

 public void set(Vector v) {
  x = v.x;
  y = v.y;
 }

 public void setX( double x ) {
  this.x = x;
 }

 public void setY( double y ) {
  this.y = y;
 }

 public Vector subtract( double c ) {
  return new Vector(this.x-c,this.y-c);
 }

 public Vector subtract( double x, double y ) {
  return new Vector(this.x-x,this.y-y);
 }

 public Vector subtract( Vector v ) {
  return new Vector(this.x-v.x,this.y-v.y);
 }

 @Override
 public String toString() {
  return String.format("(%f,%f)", x, y);
 }

 public static Vector valueOf(float x, float y) {
  return new Vector(x, y);
 }

 public static Vector valueOf(double x, double y) {
  return new Vector(x, y);
 }
}

2 comments:

Pix3l said...

Good article man :]

Nick said...

Thanks, much appreciated I hope it is useful.

Post a Comment