views:

120

answers:

5

Basically I am making a game where the enemy comes after the hero when He is in sight. What I do to achieve this is

var distx = hero.px - px;
var disty = hero.py - py;


moveX += distx * .00005;
moveY += disty * .00005;

Now I had to multiply it by .00005 because anything higher, makes him come up on the hero pretty quick. I want the enemy to move slow so I multiplied it by .00005. My problem is the farther the distance they are, the faster the monster moves which makes it harder for the hero to get away. I know why this is, I just need to know how to resolve it.

I develop the game where if the monster is too far away, i remove him from the stage. but the problem is getting away. I want him to move at a constant speed no matter how far apart they are.

I also want to achieve this by not using any class properties. (i.e I want all varibles to be local). This method is in a loop as well

What am I doing wrong? and what are solutions.

Thanks guys

A: 

This is what I came up with the seems to work ok

var distx = hero.px - px;
            var disty = hero.py - py;

            if(Math.abs(distx) > 100 && Math.abs(disty) > 100)
            {
                moveX += distx * .00001;
                moveY += disty * .00001;                
            }
            else
            {
                moveX += distx * .00005;
                moveY += disty * .00005;
            }

seems to work fine. if there is anything with less lines I can use, that would be great but I guess I will stick with something like this.

numerical25
This has the same problem as your original solution, in that you are moving the enemy by a fractional amount rather than a fixed amount.
Ipsquiggle
A: 

So you want to set a constant speed, and vary only the direction?

var speed = 1;

if (hero.px > px) moveX += speed;
else if (hero.px < px) moveX -= speed;

if (hero.py > py) moveY += speed;
else if (hero.py < py) moveY -= speed;
steve_d
That would cause the monster to move only in one of 8 directions (N/S/E/W/NE/NW/SE/SW) ...
njk
+4  A: 

All you have to do is rate limit the velocity of the enemy.

// Calculate velocity just as you're doing it
var p = .00005;
var velx = distx * p;
var vely = disty * p;

// Rate limit that velocity
var maxVel = 5; // You will have to tune this value as your max speed
var vel = Math.sqrt(velx*velx + vely*vely);
if (vel > maxVel) {
    var r = maxVel / vel;
    velx *= r;
    vely *= r;
}

// Move the enemy
moveX += velx;
moveY += vely;

You have essentially implement only the "proportional" part of what control systems engineers (1) call a PID controller. Your desire is for the enemy to track the player, and the PID controller is a nice simple tunable (think enemy difficulties) tracking controller.

In a tracking PID controller, you first measure the error between your desired goal and the current state of affairs. (You do this now, and called the variables distx and disty.) In a PID controller, you keep a memory of past errors so that you can act differently based upon different perspectives of the error.

PID = function() {
    this.p = 0;
    this.i = 0;
    this.d = 0;
    this.reset(0);
};

PID.prototype.reset = function(error) {
    this.error = error;
    this.integral = 0;
    this.differential = 0;
}   

PID.prototype.step = function(error) {
    this.integral += error;
    this.differential = (error - this.error);
    this.error = error;
    var control = this.p * this.error +
                  this.i * this.integral +
                  this.d * this.differential;
    return control;
};

You create a PID controller for each of the controllable states for the object you are trying to control (the enemy). So your update logic will change to:

// distx and disty are calculated as normal
var velx = pidx.step(distx);
var vely = pidy.step(disty);

moveX += velx;
moveY += vely;

You can now get a variety of behaviors out of enemies by varying the p,i,d parameters of the PID object. To replicate your current AI, you would set:

pidx.p = 0.00005;
pidx.i = 0;
pidx.d = 0;
// same for pidy

While a more interesting AI can be achieved by bumping up the i parameter and reducing p even further:

pidx.p = 0.000005;
pidx.i = 0.0005;
pidx.d = 0;

Every so often you will want to clear the enemy's memory if he lasts for a very long time. Just call pidx.reset(distx) to reset that memory.

Also you will still want to rate-limit the velx, vely as given earlier in this answer.

What are the benefits of all this complexity? You get enemies that behave very naturally as compared to simpler maths. I also wanted to point out that you're moving along the right tracks with your distx * 0.00005 logic.

(1) Think of controls engineers as those who study the behavior of closed systems and use that knowledge to force the system to act in ways they desire.

Frank Krueger
+3  A: 

Use some trigonometry to compute the angle you want to move, and then move at a constant speed in that direction...

disty and distx are the legs of a right triangle, the hypotenuse is sqrt(disty*disty + distx*ditsx). sin(angle) = disty/hypotenuse and cos(angle) = distx/hypotenuse (assuming x and y rise up and to the right).

var distx = hero.px - px;
var disty = hero.py - py;
var hyp = Math.sqrt(disty*disty + distx*ditsx);
moveX += speed * distx / hyp;
moveY += speed * disty / hyp;
njk
A: 

What am I doing wrong?

As the hero and the enemy move farther apart, the distance between them (naturally) becomes larger. You then take a fraction of this distance (1/20000th to be precise) and move the enemy by it. But that means that as the distance grows or shrinks, the amount you move the enemy correspondingly grows or shrinks.

As some of the other answers here state, you need to find out the direction the enemy needs to move towards the player, and then move him in that direction by a fixed amount (rather than a fractional amount).

Ipsquiggle