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.