Android Open Source - Slime-Volleyball-for-Android Physics Engine






From Project

Back to project page Slime-Volleyball-for-Android.

License

The source code is released under:

Copyright (c) 2011 Philip Bjorge Permission is hereby granted, free of charge, to any person obtaining a copy of this hardware, software, and associated documentation files (the "Product"), to deal i...

If you think the Android project Slime-Volleyball-for-Android listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.philipbjorge;
/* ww w  . j  a v  a2  s  .com*/
import java.util.ArrayList;

import android.content.res.Resources;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.Log;

public class PhysicsEngine {
  private ArrayList<RigidBody> bodies;
  private RigidBody p1,p2;
  private RigidBody ball;
  private Rect playArea;
  // ball,p1,p2 sprite
  
  private float gravity;
  
  private float rightBound;
  private float leftBound;
  private float middleBound;
  private float bottomBound;
  
  private float p1JumpTimer, p2JumpTimer, ballTimer;
  
  AnimatedSprite s_ball, s_p1, s_p2;
  
  public PhysicsEngine(RigidBody p1, RigidBody p2, float g, Resources res)
  {
    bodies = new ArrayList<RigidBody>();
    this.p1 = p1;
    this.p2 = p2;
    
    // create p1,p2,ball sprites, include set rotation for ball
    s_p1 = new AnimatedSprite(R.drawable.green_slime, 200, 100, p1.getRadius()*2, p1.getRadius(), 4, 4, res, false);
    s_p2 = new AnimatedSprite(R.drawable.blue_slime, 200, 100, p2.getRadius()*2, p2.getRadius(), 4, 4, res, false);
    
    
    
    gravity = g;
    p1JumpTimer = 0;
    p2JumpTimer = 0;
    ballTimer = 0;
  }
  
  public void addBody(RigidBody r, Resources res) {
    bodies.add(r);
  }
  
  public void addBall(RigidBody b, Resources res) {
    ball = b;
    s_ball = new AnimatedSprite(R.drawable.ball, 64, 64, ball.getRadius()*2, ball.getRadius()*2, 1, 0, res, true);
  }
  
  public void setPlayArea(Rect r) {
    playArea = r;
    middleBound = playArea.width()/2; // adjust -p1.getRadius()
    leftBound = playArea.left+p1.getRadius();
    rightBound = playArea.right - p2.getRadius();
    bottomBound = playArea.bottom-(int)(playArea.height()*.18);
  }
  
  public void offsetP1(float dx, float dy) {
    //p1.offsetVelocity(dx, dy);
  }
  
  public void setP1VX(float dx) {
    p1.setVX(dx);
  }
  
  public void setP1VY(float dy) {
    p1.setVY(dy);
  }
  
  public void setP2VY(float dy) {
    p2.setVY(dy);
  }
  
  public void setP2VX(float dx) {
    p2.setVX(dx);
  }
  
  public void startP2Jump() {
    if(P2isGrounded())
      p2JumpTimer = 15;
  }

  public boolean P2isGrounded() {
    return (p2.getY() >= bottomBound);
  }
  
  public boolean P1isGrounded() {
    return (p1.getY() >= bottomBound);
  }
  
  public void startP1Jump() {
    if(P1isGrounded())
      p1JumpTimer = 15;
  }
  
  public void startBall() {
    if(!ballIsGrounded())
      ballTimer = 15;
  }
  
  public boolean ballIsGrounded() {
    return (ball.getY()+ball.getRadius() >= bottomBound);
  }
  
  public void drawBodies(Canvas c) {
    // Loop through bodies and draw the shapes
    //c.drawPath(p1.getDrawable(), p1.getPaint());
    //c.drawPath(p2.getDrawable(), p2.getPaint());
    //c.drawPath(ball.getDrawable(),ball.getPaint());
    for(RigidBody body : bodies) {
      c.drawPath(body.getDrawable(), body.getPaint());
    }
    
    // Draw animations
    c.drawBitmap(s_p1.getBitmap(), p1.getSpriteRect().left, p1.getSpriteRect().top, null);
    c.drawBitmap(s_p2.getBitmap(), p2.getSpriteRect().left, p2.getSpriteRect().top, null);
    c.drawBitmap(s_ball.getBitmap(), ball.getSpriteRect().left, ball.getSpriteRect().top, null);
    //c.drawBitmap(s_p1.getBitmap(), new Rect(0, 0, 200, 100), p1.getSpriteRect(), null);
    //c.drawBitmap(s_p2.getBitmap(), new Rect(0, 0, 200, 100), p2.getSpriteRect(), null);
    //c.drawBitmap(s_ball.getBitmap(), new Rect(0, 0, 64, 64), ball.getSpriteRect(), null);
  }
  
  private void keepPlayersInBounds() {
    PointF p1Vel = p1.getVelocity();
    PointF p2Vel = p2.getVelocity();
    
    
    float p1NX = p1.getX() + p1Vel.x;
    float p1NY = p1.getY() + p1Vel.y;
    
    float p2NX = p2.getX() + p2Vel.x;
    float p2NY = p2.getY() + p2Vel.y;
    
    // Collision with the walls
    if(p1NX <= middleBound-p1.getRadius()-2 && p1NX >= leftBound)
    {
      p1.offsetPosition(p1Vel.x, 0);
    } else {
      if(p1NX <= middleBound-p1.getRadius()-2)
        p1.offsetPosition(leftBound-p1.getX(), 0);
      else
        p1.offsetPosition(middleBound-p1.getRadius()-2-p1.getX(), 0);
    }
    // Floors
    if(p1NY <= bottomBound)
      p1.offsetPosition(0, p1Vel.y);
    else if(p1NY > bottomBound)
      p1.offsetPosition(0, bottomBound-p1.getY());
    
    
    if(p2NX >= middleBound+p2.getRadius()+2 && p2NX <= rightBound) {
      p2.offsetPosition(p2Vel.x, 0);
    } else {
      if(p2NX >= middleBound+p2.getRadius()+2)
        p2.offsetPosition(rightBound-p2.getX(), 0);
      else
        p2.offsetPosition(middleBound+p2.getRadius()+2-p2.getX(), 0);
    }
    
    if(p2NY <= bottomBound)
      p2.offsetPosition(0, p2Vel.y);
    else if(p2NY > bottomBound)
      p2.offsetPosition(0, bottomBound-p2.getY());
  }
  
  private void applyPlayersGravity() {
    if(p1JumpTimer > 0) {
      p1JumpTimer--;
      if(p1JumpTimer < 10)
        setP1VY(-gravity * (.5f));
      else
        setP1VY(-gravity);
    } else if(p1JumpTimer > -15){
      p1JumpTimer--;
      if(p1JumpTimer > -5)
        setP1VY(gravity * (.5f));
      else
        setP1VY(gravity);
    } else {
      setP1VY(0);
    }
    
    if(p2JumpTimer > 0) {
      p2JumpTimer--;
      if(p2JumpTimer < 10)
        setP2VY(-gravity * (.5f));
      else
        setP2VY(-gravity);
    } else if(p2JumpTimer > -15){
      p2JumpTimer--;
      if(p2JumpTimer > -5)
        setP2VY(gravity * (.5f));
      else
        setP2VY(gravity);
    } else {
      setP2VY(0);
    }
  }
  
  public PointF getBallPos() {
    return new PointF(ball.getX(), ball.getY());
  }
  
  public PointF getP2Pos() {
    return new PointF(p2.getX(),p2.getY());
  }
  
  private void applyBallGravity() {
    if(ballTimer > 0) {
      ballTimer--;
      if(ballTimer < 10)
        ball.setVY(-gravity*(.5f));
      else
        ball.setVY(-gravity);
    } else if(ballTimer > -15) {
      ballTimer--;
      if(ballTimer > -5)
        ball.setVY(gravity*(.5f));
      else
        ball.setVY(gravity);
    } else {
      ball.setVY(gravity);
    }
  }
  
  // 100 on no collision
  // <=1 collision within next frame
  private float timeToCollide(RigidBody p) {
    float time = -1;
    
    float pdx = ball.getX()-p.getX();
    float pdy = ball.getY()-p.getY();
    PointF ballV = ball.getVelocity();
    PointF playerV = p.getVelocity();
    float pddx = ballV.x - playerV.x;
    float pddy = ballV.y - playerV.y;
    float radiusSquared = ball.getRadius() + p.getRadius();
    radiusSquared *= radiusSquared;
    
    float a = pddx*pddx + pddy*pddy;
    float b = 2 * ( pdx*pddx + pdy*pddy);
    float c = (pdx*pdx) + (pdy*pdy) - radiusSquared;
    float det =  (b*b) - (4*a*c);
    if(a != 0.) {
      float t = (float)((-b - Math.sqrt(det)) / (2. * a));
      if(t>=0)
      {
        if(t <= 1) {
          ball.offsetPosition(ballV.x*(0.5f*t), ballV.y*(0.5f*t));
        }
        time = t;
      }
    }
    if(time == -1)
      time = 100;
    return time;
  }
  
  private void doBallCollision(RigidBody p) {
        int dx = (int) (2 * (ball.getX() - p.getX()));
        int dy = (int) (ball.getY() - p.getY());
        int dist = (int) Math.sqrt(dx * dx + dy * dy);
        
        float tx = p.getVelocity().x;
       /* if(tx == 0) {
          if(dx > 0.05) {
            tx = -playArea.width()/220;
          } else if(dx < -0.05) {
            tx = playArea.width()/220;
          } else {
            tx = 0;
          }
        }*/
        
        int dvx = (int) (ball.getVelocity().x - tx);
        
        float ty = p.getVelocity().y;
       /* if(ty == 0)
          ty = -.5f*gravity;*/
        
        int dvy = (int) (ball.getVelocity().y - ty);
        float ballVX = ball.getVelocity().x;
            int something = (dx * dvx + dy * dvy) / dist;
            // cap the velocity
            if (something <= 0) {
                ballVX += p.getVelocity().x - (2 * dx * something) / dist;
                if (ballVX < -1.8*gravity) {
                    ballVX = (float) (-1.8*gravity);
                }
                if (ballVX > 1.8*gravity) {
                    ballVX = (float) (1.8*gravity);
                }
            }
    
    ball.setVX(ballVX);
    
    float vy = ball.getVelocity().y;
    if(p.getVelocity().y < 0)
      vy += 3*p.getVelocity().y;
    else
      vy = -gravity;
    ball.setVY(vy);
    
    ballTimer = 15;
  }
  
  private void ballCollision() {
    // if collision with moving slime, ballTimer=15 (non-moving = 8), adjust velocities for integration
    // if collision with wall, ballTimer=8 (or something), adjust velocities for integration
    // integrate
    if(!ballIsGrounded()) {
      
      PointF ballV = ball.getVelocity();
      
      if(timeToCollide(p1) <= 1) {
        doBallCollision(p1);
        // TODO: Animate player 1
      } else if(timeToCollide(p2) <= 1) {
        doBallCollision(p2);
        // TODO: Animate player 2
      } else {
        // floors
        // floor
        if (ball.getY()+ballV.y <= playArea.bottom-(2*ball.getRadius())) {
          ball.offsetPosition(0, ballV.y);
        } else {
          ball.offsetPosition(0, playArea.bottom-(2*ball.getRadius())-ball.getY());
        }
      }
      
      // Wall Collision
      if(ball.getX()+ballV.x <= playArea.left+ball.getRadius())
      {
        ball.setVX(-ballV.x);
      } else if (ball.getX()+ballV.x >= playArea.right-ball.getRadius()) {
        ball.setVX(-ballV.x);
      }
      
      // Net collision
      RigidBody net = bodies.get(0);
      
      float bNX = ball.getX()+ballV.x;
      float bNY = ball.getY()+ballV.y+ball.getRadius();
      
      if( (bNY) >= net.getY() && bNX >= net.getX()-2-ball.getRadius() && bNX <= net.getX()+2+ball.getRadius() ) {
        // TODO: Animate net collision
        if(bNX < net.getX()) {
          // hit left
          ball.offsetPosition(net.getX()-ball.getX()-ball.getRadius()-2, 0);
          ball.setVX(-ballV.x);
        } else {
          // hit right
          ball.offsetPosition(net.getX()-ball.getX()+ball.getRadius()+2, 0);
          ball.setVX(-ballV.x);
        }
      } 
      ball.offsetPosition(ball.getVelocity().x, 0);
    } else {
      // Ball on the ground.
      // TODO: Update score
      // Reset match.
      ball.offsetPosition(playArea.width()/4-ball.getX(), -300);
      ball.setVX(0);
      
      p1JumpTimer = -16;
      p2JumpTimer = -16;
      
      p1.resetPos();
      p2.resetPos();
    }
  }
  
  public void integrate(float dt) {
    // apply ball gravity
    applyBallGravity();
    // check ball collision, adjust velocity, integrate
    ballCollision();
    
    applyPlayersGravity();
    keepPlayersInBounds();
    
    s_p1.update();
    s_p2.update();
    s_ball.update();
  }
}




Java Source Code List

com.philipbjorge.AI.java
com.philipbjorge.AnimatedSprite.java
com.philipbjorge.GameThread.java
com.philipbjorge.GameView.java
com.philipbjorge.Game.java
com.philipbjorge.Match.java
com.philipbjorge.PhysicsEngine.java
com.philipbjorge.RigidBody.java
com.philipbjorge.Settings.java
com.philipbjorge.SlimeVolleyball.java