Processing Tower Defence Game - Towers attacking Enemies - java

I will keep this short, I am making a Tower Defence game as a mini project while I have some spare time and I am trying to figure out how I can implement the towers to be able to shoot the enimies when they come into range using dist but I just don't know how to implement a method that uses the enimies position and the towers position. I have an ArrayList of CreepSprites and Towers
CreepSprite[] CreepSprites;
ArrayList<Tower> AllTowers = new ArrayList<Tower>();
ArrayList<Creep> AllCreeps = new ArrayList<Creep>();
If someone could give me some guidence as to how I would go about making the towers able to shoot the Creeps that would be great, even if it doesn't get rid of them, just able to shoot at them would be great.
Cheers

#GoneUp's answer is in the right direction. In Processing you have a class called PVector which provides a distance method as well: dist()
PVector towerPos = new PVector(100, 100);
PVector enemyPos = new PVector(300, 300);
float towerAttackRadius = 200;
void setup() {
size(400, 400);
rectMode(CENTER);
strokeWeight(3);
}
void draw() {
background(255);
//draw tower
noFill();
//check range
if(towerPos.dist(enemyPos) < towerAttackRadius){
//tower engaged, draw in green
stroke(0,192,0);
}else{
//tower in idle mode, draw in blue
stroke(0, 0, 192);
}
//visualise tower attack radius
//(towerAttackRadius * 2 -> radius * 2 = diameter (width/height))
ellipse(towerPos.x, towerPos.y, towerAttackRadius * 2, towerAttackRadius * 2);
//visualise tower
rect(towerPos.x, towerPos.y, 30, 30);
//draw enemy
stroke(192, 0, 0);
rect(enemyPos.x, enemyPos.y, 10, 10);
//instructions
fill(0);
text("click and drag to move enemy",10,15);
}
void mouseDragged() {
enemyPos.set(mouseX, mouseY);
}
I warmly recommend Daniel Shiffman's Nature of Code chapter on Vectors.
It's a little bit of linear algebra, but it's super useful, especially for games so worth getting the hang of it early.
For example, to shoot a bullet, you will need to workout the direction towards.
You can do that by using vector subtraction.
Additionally, you probably want to control how fast the bullet moves on screen towards that direction. That can also be done by normalising the vector: keeping it's direction, but reducing it's size to 1.0:
After that point it's easy to scale (multiply) the vector to any size you want.
If you know the tower's position, you simply need to add this scaled velocity to compute where the bullet should be drawn each frame.
PVector already has a function that does both: setMag().
(set mag is short for set magnitude (or vector length))
It also provides a heading() function which is handy to workout the angle.
Here's a basic proof of concept:
PVector towerPos = new PVector(100, 100);
PVector enemyPos = new PVector(300, 300);
float towerAttackRadius = 300;
ArrayList<Bullet> bullets = new ArrayList<Bullet>();
void setup() {
size(400, 400);
rectMode(CENTER);
strokeWeight(3);
}
void draw() {
background(255);
//check if an enemy is within range using dist()
//if the distance is smaller than the radius, attack!
if(towerPos.dist(enemyPos) < towerAttackRadius){
//hacky frame based counter: please use a millis() based timer instead
//shoot every 30 frames
if(frameCount % 30 == 0){
shoot();
}
}
//update bullets
for(Bullet b : bullets) {
b.update();
}
//draw tower
noFill();
stroke(0, 0, 192);
//visualise tower attack radius
//(towerAttackRadius * 2 -> radius * 2 = diameter (width/height))
ellipse(towerPos.x, towerPos.y, towerAttackRadius * 2, towerAttackRadius * 2);
//visualise tower
rect(towerPos.x, towerPos.y, 30, 30);
//draw enemy
stroke(192, 0, 0);
rect(enemyPos.x, enemyPos.y, 10, 10);
//instructions
fill(0);
text("click and drag to move enemy",10,15);
}
void mouseDragged() {
enemyPos.set(mouseX, mouseY);
}
void shoot(){
//make a new Bullet pointing from the tower to the enemy
Bullet b = new Bullet(towerPos.x,towerPos.y,enemyPos.x,enemyPos.y);
//add it to the list of bullets (for updates)
bullets.add(b);
println(b);
}
class Bullet {
//where does the bullet shoot from (and it's current position)
PVector position = new PVector();
//where does the bullet shooting towards
PVector target = new PVector();
//how fast does the bullet move on screen
float speed = 1.2;
//how large goes the bullet appear on screen
float size = 10;
//bullet velocity
PVector velocity;
Bullet(float startX,float startY, float endX, float endY) {
position.set(startX,startY);
target.set(endX,endY);
//compute the difference vector (start to end) = direction
velocity = PVector.sub(target,position);
//normalize the vector = same direction but magnitude of 1 -> makes it easy to scale
//velocity.normalize();
//scale by the speed to move on screen)
//normalize + multiple = resize the vector -> same direction, different length
//velocity.mult(speed);
//or do both normalize and multiple using setMag()
velocity.setMag(speed);
}
void update() {
//update position based on velocity (simply add velocity to current position)
position.add(velocity);
//render
//compute rotation angle from velocity (implementation is 2D only btw)
float angle = velocity.heading();
pushMatrix();
translate(position.x,position.y);
rotate(angle);
stroke(0);
line(0,0,size,0);
popMatrix();
}
String toString(){
return position+"->"+target;
}
}
You can actually play with a preview bellow:
var towerPos,enemyPos;
var towerAttackRadius = 300;
var bullets = [];
function setup() {
createCanvas(400, 400);
rectMode(CENTER);
strokeWeight(3);
towerPos = createVector(100, 100);
enemyPos = createVector(300, 300);
}
function draw() {
background(255);
//check if an enemy is within range using dist()
//if the distance is smaller than the radius, attack!
if(towerPos.dist(enemyPos) < towerAttackRadius){
//hacky frame based counter: please use a millis() based timer instead
//shoot every 30 frames
if(frameCount % 30 === 0){
shoot();
}
}
//update bullets
for(var i = 0; i < bullets.length; i++) {
bullets[i].update();
}
//draw tower
noFill();
stroke(0, 0, 192);
//visualise tower attack radius
//(towerAttackRadius * 2 -> radius * 2 = diameter (width/height))
ellipse(towerPos.x, towerPos.y, towerAttackRadius * 2, towerAttackRadius * 2);
//visualise tower
rect(towerPos.x, towerPos.y, 30, 30);
//draw enemy
stroke(192, 0, 0);
rect(enemyPos.x, enemyPos.y, 10, 10);
//instructions
noStroke();
fill(0);
text("click and drag to move enemy",10,15);
}
function mouseDragged() {
enemyPos.set(mouseX, mouseY);
}
function shoot(){
//make a new Bullet pointing from the tower to the enemy
var b = new Bullet(towerPos.x,towerPos.y,enemyPos.x,enemyPos.y);
//add it to the list of bullets (for updates)
bullets.push(b);
}
function Bullet(startX,startY,endX,endY) {
//where does the bullet shoot from (and it's current position)
this.position = createVector(startX,startY);
//where does the bullet shooting towards
this.target = createVector(endX,endY);
//how fast does the bullet move on screen
this.speed = 1.2;
//how large goes the bullet appear on screen
this.size = 10;
//compute the difference vector (start to end) = direction
this.velocity = p5.Vector.sub(this.target,this.position);
//normalize the vector = same direction but magnitude of 1 -> makes it easy to scale
this.velocity.normalize();
//scale by the speed to move on screen)
//normalize + multiple = resize the vector -> same direction, different length
this.velocity.mult(this.speed);
this.update = function() {
//update position based on velocity (simply add velocity to current position)
this.position.add(this.velocity);
//render
//compute rotation angle from velocity (implementation is 2D only btw)
var angle = this.velocity.heading();
push();
translate(this.position.x,this.position.y);
rotate(angle);
stroke(0);
line(0,0,this.size,0);
pop();
}
}
//http://stackoverflow.com/questions/39698472/processing-tower-defence-game-towers-attacking-enemies
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.3/p5.min.js"></script>
Have fun with PVectors ! :)
One important note:
the above code is a proof of concept and not optimized.
On the long run with a lot of towers and enemies it will slow down.
Once you get the math/code right, you can start doing a few improvements:
manage bullets/instances (e.g. re-use bullets that are off screen
rather than creating new instances all the time)
use the squared distance via magSq() instead and the squared radius to speed up calculations

You could use the Point2D class to represent the x,y coordinates of your figures. The class got a pre-implemented distance method wich can be checked against a radius.

Related

Calculating the Value of Pi in Processing

Processing is an environment that makes use of Java. I am trying to to use the Monte Carlo method to calculate the value of Pi. I am trying to create a dartboard (a circle within a square), and return "Yes" whenever the randomly selected point is selected within the circle.
Processing uses a coordinate system where the top left corner is the origin, rightwards is the positive x-axis, and downwards is the positive y-axis.
Here's my code:
float circleX;
float circleY;
float r;
void setup() {
size(360, 360);
circleX = 50;
circleY = 50;
frameRate(0.5);
}
void draw() {
background(50);
fill(255);
stroke(255);
fill(100);
ellipse(180, 180, 360, 360);
ellipse(circleX, circleY, 10, 10);
circleX = random(360);
circleY = random(360);
r = (circleX-180)*(circleX-180) + (180-circleY)*(180-circleY);
if (r < 32400) {
print("Yes! ");
}
}
However, on many instances, points inside the circle do not return "Yes," and points outside the circle do return "Yes." Any ideas on what is wrong?
You have to swap the lines generating the random coordinates and drawing it:
// Generate new random coordinates
circleX = random(360);
circleY = random(360);
// Draw circle at those coordinates
ellipse(circleX, circleY, 10, 10);
// Check whether the coordinates are withing the big circle
r = (circleX-180)*(circleX-180) + (180-circleY)*(180-circleY);
The way you do it, the circle is drawn before you generate new coordinates, which you then check.

Making a moving circle disappear after being clicked on, Processing

I have written a program in which a UFO (in essence, a gray ellipse) appears from the center of the screen and flies to the edge. There is a laser that appears when the mouse is pressed, and disappears when the mouse is released. I want to make it so that the UFO disappears when the mouse clicks on it/the laser touches it.
I've made it as far as to make the UFO class and create variables that determine its movements and speed, and I was able to get the laser to appear directly on the cursor. I thought of making an 'if' statement to check if the cursor is within the radius (or diameter) of the UFO, and placing it inside of the for loop I created for the UFOs. However, I am not sure how to achieve the proper syntax to make it happen.
Note: You may need to wait a few seconds for the first circle to appear after you play the sketch.
UFO[] ufos = new UFO[3];
void setup() {
size(700, 700);
for (int j = 0; j < ufos.length; j++) {
ufos[j] = new UFO();
}
}
//UFO class
//Class setup ends on line 61
class UFO {
float a;
float b;
float c;
float sa;
float sb;
float d;
UFO() {
//declare float a/b/c value
a = random(-width/2, width/2);
b = random(-height/2, width/2);
c = random(width);
}
//UFO movement
void update() {
//float c will serve as a speed determinant of UFOs
c = c - 1;
if (c < 5) {
c = width;
}
}
//UFO setup
void show() {
//moving x/y coordinates of UFO
float sa = map(a / c, 0, 1, 0, width);
float sb = map(b / c, 0, 1, 0, height);
float d = map(c, 0, width, 50, 0);
//UFO drawing shapes
//ellipse is always sa (or sb) / c to allow UFO to appear
//as if it is moving through space
fill(200);
ellipse((sa / c), (sb / c), d + 5, d+5);
//Hides UFO way off the screen
//and replaces it with a black-filled ellipse until
//it forms into a full circle
//When radius d becomes 50, the UFO flies from the
//center of the screen to off of the screen
if (d < 50) {
fill(0);
ellipse(-5, -10, 90, 90);
sa = 10000;
sb = 10000;
}
}
}
void draw() {
//Background
background(0);
//Translated the screen so that the UFOs appear to fly from
//the center of the screen
translate(width/2, height/2);
//UFO draw loop, make UFO visible on screen
for (int j = 0; j < ufos.length; j++) {
ufos[j].update();
ufos[j].show();
//mouse-click laser
if (mousePressed == true) {
fill(200,0,0);
ellipse(mouseX - 352,mouseY - 347,50,50);
}
}
}
Like I said on the Happy Coding forum:
Basically, if your UFO is a series of circles, then you just need to use the dist() function to check whether the distance from the mouse to the center of the circle is less than the radius of the circle. If it is, then the mouse is inside the circle. Here's a small example:
float circleX = 50;
float circleY = 50;
float circleDiameter = 20;
boolean showCircle = true;
void draw(){
background(0);
if(showCircle){
ellipse(circleX, circleY, circleDiameter, circleDiameter);
}
}
void mousePressed(){
if(dist(mouseX, mouseY, circleX, circleY) < circleDiameter/2){
showCircle = false;
}
}
If your UFO is multiple circles, then you need to apply this logic to each circle. Please try something and post a small example like this one (not your whole sketch) if you get stuck. Good luck.

How to make a ball go in a specific direction according to where it's clicked?

Table t1= new Table(300, 300);
float power=0;
float dx=0;
float dy=0;
void setup()
{
size(1000, 600);
frameRate(10);
}
void draw()
{
strokeWeight(1);
stroke(0, 0, 0);
strokeWeight(10);
stroke(255, 0, 0);
fill(26, 218, 35);
rect(0, 0, 1000, 600);
noStroke();
fill(0);
ellipse(0, 0, 80, 80);
ellipse(1000, 0, 80, 80);
ellipse(0, 600, 80, 80);
ellipse(1000, 600, 80, 80);
strokeWeight(1);
stroke(0, 0, 0);
fill(255);
ellipse(t1.cue_ball.center.x, t1.cue_ball.center.y, 20, 20);
dx=friction(dx);
dy=friction(dy);
if (mousePressed)
{
power+=5;
}
if (t1.cue_ball.center.x+30>1000 || t1.cue_ball.center.x-30<0)
{
dx*=-1;
}
if (t1.cue_ball.center.y+30 >=600 || t1.cue_ball.center.y -30<=0)
{
dy*=-1;
}
t1.cue_ball.center.x +=dx;
t1.cue_ball.center.y +=dy;
}
void mouseReleased()
{
dx=power*2;
dy=power*2;
}
float friction (float c)
{
c*=0.9;
return c;
}
class Ball
{
float rad;
Point center;
Point contact_point;
color col;
Ball ( float a, float b)
{
center = new Point (a+=dx, b+=dy);
//contact_point= new Point(
}
}
class Table
{
Ball [] b_arr;
Stick st;
Ball cue_ball;
Table ( float a, float b )
{
cue_ball= new Ball( a, b);
}
}
class Point
{
float x;
float y;
Point(float a, float b)
{
x=a;
y=b;
}
}
class Stick
{
Point start_p;
Point end_p;
color col;
int length;
}
So we want to add something so that when the ball is clicked, it will move accordingly. For example, if it is clicked in the top left, it will move diagonally right down. If clicked bottom left, it will move diagonally right up. Also, is there a way to correspond this to the angle? So a larger angle between the click point and the center will make a steeper diagonal.
Added lines of code I'm not sure where needs to be added:
t1.cue_ball.center.x+=dx;
t1.cue_ball.center.y+=dy;
dx=t1.cue_ball.center.x-mouseX;
dy=t1.cue_ball.center.y-mouseY;
float n= sqrt(pow(dx,2)+pow(dy,2));
dx*=power/n;
dy*=power/n;
If you have or know how to compute the angle between the x-axis and the cue (I'm assuming this is billiards), then to make the ball go in that direction, if I understand your code correctly, you could just set the dx and dy of the ball that you strike according to
dx = power*cos(angle)
dy = power*sin(angle)
You might have to take the negative angle instead, depending on the coordinate system (if going up is positive or negative in the y-direction), and precisely what angle you compute. The easiest is probably to just plug it in and see what happens!
EDIT:
Not related to your question, but as a matter of style, it might be a good idea to move your logic for moving and drawing the ball to the Ball class. So that every tick, you draw the balls on the screen by calling an appropriate draw() method for each instance of the Ball class. Then it would be much easier to move several balls at once.
EDIT2:
I just realized you can actually solve the problem without trigonometry, if you know the point where you click. Let's say cx,cy is the point where you click, and x,y is the center of the ball, then your dx and dy for the ball can be computed as:
dx = x-cx
dy = y-cy
n = sqrt(dx^2 + dy^2)
dx *= power/n
dy *= power/n
Explanation:
The outgoing velocity for the ball should be in the same direction as the click relative to the ball. So we already have the relative lengths of dx and dy, and to get the right power we just need to normalize and multiply by the power.

how can i make a graphic object solid in java?

I'm working on a simple game and i need these squareBumpers which simply stands idle and when got hit, collides and reflects the ball. But currently the ball just flies through my squareBumpers. I can only use java awt and swing libraries. Here's the code:
class squareBumper {
private int x = 300;
private int y = 300;
private Color color = new Color(66,139,139);
public void paint(Graphics g) {
Rectangle clipRect = g.getClipBounds();
g.setColor(color);
g.fillRect(x, y, 31, 31);
}
}
class BouncingBall {
// Overview: A BouncingBall is a mutable data type. It simulates a
// rubber ball bouncing inside a two dimensional box. It also
// provides methods that are useful for creating animations of the
// ball as it moves.
private int x = 320;
private int y = 598;
public static double vx;
public static double vy;
private int radius = 6;
private Color color = new Color(0, 0, 0);
public void move() {
// modifies: this
// effects: Move the ball according to its velocity. Reflections off
// walls cause the ball to change direction.
x += vx;
if (x <= radius) { x = radius; vx = -vx; }
if (x >= 610-radius) { x = 610-radius; vx = -vx; }
y += vy;
if (y <= radius) { y = radius; vy = -vy; }
if (y >= 605-radius) { y = 605-radius; vy = -vy; }
}
public void randomBump() {
// modifies: this
// effects: Changes the velocity of the ball by a random amount
vx += (int)((Math.random() * 10.0) - 5.0);
vx = -vx;
vy += (int)((Math.random() * 10.0) - 5.0);
vy = -vy;
}
public void paint(Graphics g) {
// modifies: the Graphics object <g>.
// effects: paints a circle on <g> reflecting the current position
// of the ball.
// the "clip rectangle" is the area of the screen that needs to be
// modified
Rectangle clipRect = g.getClipBounds();
// For this tiny program, testing whether we need to redraw is
// kind of silly. But when there are lots of objects all over the
// screen this is a very important performance optimization
if (clipRect.intersects(this.boundingBox())) {
g.setColor(color);
g.fillOval(x-radius, y-radius, radius+radius, radius+radius);
}
}
public Rectangle boundingBox() {
// effect: Returns the smallest rectangle that completely covers the
// current position of the ball.
// a Rectangle is the x,y for the upper left corner and then the
// width and height
return new Rectangle(x-radius, y-radius, radius+radius+1, radius+radius+1);
}
}
Take a look at the classes that implement the Shape interface. There are ellipses and other shapes, and they all implement a intersects(Rectangle2D) method. It might help you if you don't want to perform intersection yourself.
As for dealing with the collision, well, it depends on the level of accuracy you want. Simply deflecting the ball of edges is quite easy. Just determine whether the collided side of the rectangle is vertical or horizontal, and negate the corresponding velocity component accordingly. If you want to handle the corners, well that is a bit more complicated.
You need to detect when the ball has collided with the bumper. You have the boundingBox() method of BouncingBall, this will get you a rectangle that contains your ball. So you need to check if this rectangle intersects your square bumper (which implies a collision), and then do something with that.

Rectangle collision detection algorithm half working

I am writing an algorithm for collision detection in slick2d + java that I will eventually use in a platformer game that I will be making. How the algorithm works is it detects how much the player is overlapping a rectangle and then moves the player out of the rectangle by that overlap. The problem is, the algorithm has quite a few issues that I can't figure out how to fix. First, sometimes the player move too far out of the rectangle so it looks it is bouncing off it. Secondly sometimes the player is able to move a small but noticeable amount inside of the rectangle. Lastly, if I increase the velocity sometimes the player can pass all the way through the rectangle. This is quite a vague question but I really need some help figuring out what is wrong. Any ideas at all would be greatly appreciated. The source code should compile without any problems if you have Slick2D installed.
Algorithm:
public void Collision(Polygon player, Polygon poly, Vector2f translation){
Vector2f magnitude = new Vector2f();
//Find the vectre of each object
Vector2f p1Centre = new Vector2f(player.getX() + (player.getWidth()/2), player.getY() + (player.getHeight()/2));
Vector2f p2Centre = new Vector2f(poly.getX() + (poly.getWidth()/2), poly.getY() + (poly.getHeight()/2));
//Calculate the distance between the two
Vector2f distance = new Vector2f(p1Centre);
distance.sub(p2Centre);
//Get the absolute distance
Vector2f absDistance = new Vector2f(distance.x<0 ? -distance.x : distance.x, distance.y<0 ? -distance.y : distance.y);
//Get the combined half widths and heights of each object
Vector2f halvedBounds = new Vector2f((player.getWidth() + poly.getWidth())/2.0f, (player.getHeight() + poly.getHeight())/2.0f);
//If the absolute distance is less thate the halved widths heights then there is a collision
if((absDistance.x < halvedBounds.x) && (absDistance.y < halvedBounds.y)){
//Set the magnitude vector to the halved bounds minus the absolute distance
magnitude.x = halvedBounds.x - absDistance.x;
magnitude.y = halvedBounds.y - absDistance.y;
//Only react to the lesser overlap;
if(magnitude.x < magnitude.y){
magnitude.x = (distance.x > 0) ? magnitude.x : -magnitude.x;
magnitude.y = 0;
}
else{
magnitude.y = (distance.y > 0) ? magnitude.y : -magnitude.y;
magnitude.x = 0;
}
//Debug
System.out.println(magnitude.x+" "+magnitude.y);
System.out.println(translation.x+" "+translation.y+"\n");
//Add the magnitude to the player position
position.add(magnitude);
}
}
Full Source:
import java.util.ArrayList;
import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.geom.Polygon;
import org.newdawn.slick.geom.Vector2f;
public class TestCode extends BasicGame {
private Vector2f position = new Vector2f(300, 300);
private ArrayList<Polygon> solids;
private Polygon player;
public TestCode(String title) {
super(title);
}
public static void main(String[] args) throws SlickException{
AppGameContainer game = new AppGameContainer(new TestCode("test"));
game.setDisplayMode(800, 600, false);
game.start();
}
#Override
public void render(GameContainer gc, Graphics g) throws SlickException {
if(gc.isPaused()){
g.setColor(Color.red);
g.drawString("Paused", 90, 10);
}else{
g.setColor(Color.green);
g.drawString("Playing", 90, 10);
}
g.setColor(Color.red);
for(Polygon p : solids)
g.fill(p);
g.setColor(Color.cyan);
g.fill(player);
}
#Override
public void init(GameContainer gc) throws SlickException {
gc.setVSync(true);
solids = new ArrayList<Polygon>();
player = new Polygon(new float[]{
50, 50, // upper left point
70, 50, // upper right
70, 90, // lower right
50, 90 // lower left
});
for(int i=0, x=200, y=200; i<10; i++, x+=40){
solids.add(new Polygon(new float[]{
x, y, // upper left point
x+40, y, // upper right
x+40, y+40, // lower right
x, y+40 // lower left
}));
}
}
#Override
public void update(GameContainer gc, int delta) throws SlickException {
Input input = gc. getInput();
Vector2f translation = new Vector2f(0, 0);
if(input.isKeyDown(Input.KEY_UP))
translation.y = -1f;
if(input.isKeyDown(Input.KEY_DOWN))
translation.y = 1f;
if(input.isKeyDown(Input.KEY_LEFT))
translation.x = -1f;
if(input.isKeyDown(Input.KEY_RIGHT))
translation.x = 1f;
translation.normalise();
translation.x*=2;
translation.y*=2;
position.add(translation);
for(Polygon p : solids)
Collision(player, p, translation);
player.setLocation(position);
}
public void Collision(Polygon player, Polygon poly, Vector2f translation){
Vector2f magnitude = new Vector2f();
//Find the vectre of each object
Vector2f p1Centre = new Vector2f(player.getX() + (player.getWidth()/2), player.getY() + (player.getHeight()/2));
Vector2f p2Centre = new Vector2f(poly.getX() + (poly.getWidth()/2), poly.getY() + (poly.getHeight()/2));
//Calculate the distance between the two
Vector2f distance = new Vector2f(p1Centre);
distance.sub(p2Centre);
//Get the absolute distance
Vector2f absDistance = new Vector2f(distance.x<0 ? -distance.x : distance.x, distance.y<0 ? -distance.y : distance.y);
//Get the combined half widths and heights of each object
Vector2f halvedBounds = new Vector2f((player.getWidth() + poly.getWidth())/2.0f, (player.getHeight() + poly.getHeight())/2.0f);
//If the absolute distance is less thate the halved widths heights then there is a collision
if((absDistance.x < halvedBounds.x) && (absDistance.y < halvedBounds.y)){
//Set the magnitude vector to the halved bounds minus the absolute distance
magnitude.x = halvedBounds.x - absDistance.x;
magnitude.y = halvedBounds.y - absDistance.y;
//Only react to the lesser overlap;
if(magnitude.x < magnitude.y){
magnitude.x = (distance.x > 0) ? magnitude.x : -magnitude.x;
magnitude.y = 0;
}
else{
magnitude.y = (distance.y > 0) ? magnitude.y : -magnitude.y;
magnitude.x = 0;
}
//Debug
System.out.println(magnitude.x+" "+magnitude.y);
System.out.println(translation.x+" "+translation.y+"\n");
//Add the magnitude to the player position
position.add(magnitude);
}
}
}
The issue with a high velocity and the player passing through the rectangle has to do with how often you're sampling the collision data.
Lets suppose that I'm really dumb, and I only check for collisions once a second. If an object is moving at 15 meters per second, and there's a 1 meter square in its way. If I check for collisions when the object is 7 meters away from the square, and then one second later, I'll completely miss that the object went through the square.
The way a lot of collision detection libraries deal with this is they have fast moving objects be checked more often than regular objects. How "fast" is your player moving when this happens?
So just turn your problem around: check before moving, not after. If the player would land in the rectangle don't allow him.
And here comes the more important part of your problem: how far is "just touching the surface" (and really depends on how you do your collision detection; if you are doing bounding-box collisions it translates into a calculation, if you are doing pixel-perfect collisions you might have to test a few what-if cases).
Since this happens all the time, try to avoid unnecessary work (why test pixel by pixel, interval splitting might be better approach; do bounding box calculations first, pixel perfect then; for "rectangular enough" objects bounding box ~~ pixel pirfect ...)
Welcomme to the world of programming, it's more about solving problems, not so much about slapping a buch of "paint a spire, move it by x pixels & goto 10" statements together ;-)

Categories