I'm creating a small game in Node.js/Socket.IO and need some advice on creating the AI. The code I have below is a real quick example I came up with but it's so fast the player doesn't even see the enemy move on the client-side. Am I on the right lines with this approach or is there a better way I should be doing it?
Thanks!
var random;
setInterval(function() {
random = Math.round(Math.random() * 200);
move(random, random);
console.log("Moving player");
}, 10000)
var move = function(targetX, targetY) {
if (x < targetX) {
while (x < targetX) {
x++;
sendNewCoordinates(x, y);
}
} else if (x > targetX) {
while (x > targetX) {
x--;
sendNewCoordinates(x, y);
}
} else if (y < targetY) {
while (y < targetX) {
y++;
sendNewCoordinates(x, y);
}
} else if (y > targetY) {
while (y > targetX) {
y--;
sendNewCoordinates(x, y);
}
}
};
var sendNewCoordinates = function(newX, newY) {
socket.sockets.emit("move enemy", {x: newX, y: newY});
};
That's actually a pretty good AI! Randomizing the interval between movements is a very easy, common technique for things like this. Im curious and would love to try whatever you are making! One thing to be aware of though, is to make sure the AI is not TOO good.
One other thing you could implement in your code is to have your AI "aim" for a point slightly away from the target. Eg:
var move = function(targetX + randomX, targetY + randomY)
You can also use the position of the target before it moved to predict where it is heading.
var xChange = (targetX2 - targetX1)/(timeInterval1);
var yChange = (targetY2 - targetY1)/(timeInterval1);
var move = function(targetX + xChange * timeInterval2, targetY + yChange * timeInterval2)
where timeInterval1 is the time interval between two of the targets positions and timeInterval2 is the time interval between your current position and your next position.
The key is not making the AI too hard for the player though. ;)
Related
I'm making a game where the player will move using their cursor and will have to dodge objects. Each object has a hitbox, so I want to be able to update the number of lives whenever the cursor hits one. I'm just not sure what an efficient way is to keep track of all the hitboxes. I'd also like to record the object that the cursor collided with, so that bonus objects can be counted. The objects constantly respawn and move off the canvas, so I think keeping an array of all the hitboxes would pretty quickly get out of hand. Here's some code with some pseudo code for what I want to do.
canvas.addEventListener("mousemove", e => {
mouseX = e.clientX - rect.left;
mouseY = e.clientY - rect.top;
if (somehitboxX<=mouseX<=somehitboxX + 50 and somehitboxY<=mouseY<=somehitboxY + 50){
doSomething
}
});
EDIT: Instead of having my cursor keep track of all the hitboxes, would it be possible to have the hitboxes keep track of the cursor? Maybe by adding an event listener to each object? If that's possible, how would I go about doing it?
EDIT: Here's some code to get a better idea of what I'm working with. The emoji objects are what need to be dodged by the player. Each one has an emoji, a score, coordinates, speed, a hitbox, and a boolean that says whether it's been hit or not (though I'm not sure how to control it yet since that's what I need help with).
function Emoji(emojicon, score, x, y, speed) {
this.emojicon = emojicon;
this.score = score;
this.x = x;
this.y = y;
this.speed = speed;
this.hitbox = { hbx: x, hby: y - 50, hbWidth: 50, hbHeight: 50 };
this.hit = false;
}
The emoji objects are initialized like this:
var xPos = 0;
for (var i = 0; i < 9; i++) {
var emo = new Emoji("😜", 5, xPos, 520, 1);
emojis.push(emo);
xPos += 55;
}
emojis.forEach(emoji => drawEmoji(emoji));
}
And then Emoji.prototype.update updates both the coordinates of the emoji and its hitbox:
Emoji.prototype.update = function() {
if (this.y < 520) {
this.y -= this.speed;
this.hitbox.hby = this.y - 50;
} else {
var flip = Math.floor(Math.random() * 1000);
if (flip == 1) {
this.y -= this.speed;
this.hitbox.y = this.y - 50;
}
}
};
I hope this is enough code to get an idea of what I have so far.
I created a sprite which moves in a circular motion. I want to change the direction if the mouse button (touch) is clicked, but when the mouse is clicked, the direction does not change.
This is my code:
create: function() {
this.stage.backgroundColor = '#FFFFFF';
x = this.world.centerX;
y = this.world.centerY;
this.direction = 1;
this.speedDelta = 0.002;
this.radius = 114;
this.physics.startSystem(Phaser.Physics.ARCADE);
//adding player
this.player = this.add.sprite(x, y, 'player');
this.player.anchor.setTo(0.5, 0.5);
this.game.physics.arcade.enable(this.player);
this.input.onDown.add(this.changeDirection, this);
},
update: function() {
if (this.direction == 1) {
this.speedDelta = 0.002;
} else if (this.direction == 1) {
this.speedDelta = -0.002;
}
var period = this.time.now * this.speedDelta;
this.player.x = Math.cos(period) * this.radius;
this.player.y = d + Math.sin(period) * this.radius;
},
changeDirection: function() {
this.direction = -this.direction;
}
}
Your basic assumptions about the behavior of cos and sin are incorrect. You can't simply change the sign of the input and get a different answer.
Notice:
cos(pi/4) = 0.707
cos(-pi/4) = 0.707
sin(pi/4) = -0.707
sin(-pi/4) = -0.707
Also I think your code would benefit by using a slightly different approach in general.
Currently you're recalculating the position from scratch on every update cycle. To get the behavior you want, I think it would be simpler to instead calculate a location delta based off of the speed and direction, then simply add the delta to the current location.
That would also allow you to eliminate your conditional statement, which will make the code cleaner.
I have an image of a ball, a cup, and an inner and outer div that represent a throw power bar.
When the user clicks the ball, the power bar starts to increment and then decrements. When the user clicks the ball a 2nd time, the throw power bar stops and the ball is thrown.
As I have began coding this, I realized certain things are going to be extremely complicated, even though the idea itself is rather simple.
For example, I want the ball to be able to "bounce", meaning I will need to not only keep track of the balls x and y coordinates, but also a z coordinate representing depth.
When the ball falls to bounce, the z coordinate with be decremented, and the image of the ball should be scaled down in size, and when it begins bouncing back up, it should scale up in size, again based on the z coordinate. I also want the ball to bounce off the cup if the z coordinate is below a certain value by the time it reaches the cup, go into the cup if it is a certain sweet spot, or go over the cup if it's z value is over that sweet spot.
In the interest of keeping this somewhere short, I'll just post what I have so far. This example is lacking certain things that I was hoping people here could help me with.
http://jsfiddle.net/7Lsh78nw/
<html>
<head>
<style>
#ball {
position:absolute;
width:75px;
height:75px;
}
#cup1 {
position:absolute;
left:375px;
}
#outerPowerMeter {
position:absolute;
width:25px;
height:100px;
background-color:red;
}
#innerPowerMeter {
position:absolute;
width:25px;
height:100px;
background-color:black;
}
</style>
<script>
window.onload = function() {
var ball = document.getElementById("ball");
var yPos = 500;
var xPos = 400;
var zPos = 100;
var ballWidth = 75;
var ballHeight = 75;
var throwBallInterval;
var changeBallSizeInterval;
ball.style.top = yPos + "px";
ball.style.left = xPos + "px";
var cup1 = document.getElementById("cup1");
var powerMeter = document.getElementById("innerPowerMeter");
var powerMeterValue = 0;
var powerMeterHeight = 100;
var powerMeterActive = false;
var powerMeterInterval;
powerMeter.style.height = powerMeterHeight + "px";
ball.onclick = function() {
if (powerMeterActive == false) {
powerMeterActive = true;
startPowerMeter();
} else {
powerMeterActive = false;
stopPowerMeter();
throwBall();
}
}
function throwBall() {
throwBallInterval = setInterval(function() {
yPos = yPos - 1;
ball.style.top = yPos + "px";
}, 1);
changeBallSizeInterval = setInterval(function() {
zPos = zPos - 1;
ballWidth = ballWidth - 1;
ballHeight = ballHeight - 1;
ball.style.width = ballWidth;
ball.style.height = ballHeight;
}, 100);
}
function startPowerMeter() {
var increment = true;
powerMeterInterval = setInterval(function() {
if (increment == true) {
powerMeterValue = powerMeterValue + 1;
powerMeter.style.height = (powerMeterHeight - powerMeterValue) + "px";
if (powerMeterValue == 100) {
increment = false;
}
} else {
powerMeterValue = powerMeterValue - 1;
powerMeter.style.height = (powerMeterHeight - powerMeterValue) + "px";
if (powerMeterValue == 0) {
increment = true;
}
}
},1);
}
function stopPowerMeter() {
clearInterval(powerMeterInterval);
}
function detectCollision() { }
function detectGoal() { }
}
</script>
</head>
<body>
<img id="cup1" src="http://beerwar.com/game/images/cup.png">
<img id="ball" src="http://beerwar.com/game/images/ball.png">
<div id="outerPowerMeter">
<div id="innerPowerMeter"></div>
</div>
</body>
</html>
Since you posted such a detailed case, i thought i give you some pointers. Mind you: this is mostly vector math. I'm not a physicist either, but vector math isn't that complicated luckily! Some pythagoras here and there and you are set.
A good an fast library for that is glMatrix
A couple of things to get you going. Please note: it is pseudo code, but it does explain the concept of it.
Keep a vector for the position of the ball
Keep a vector for the position of the cup (where the ball should hit)
Keep a vector for the position of 'the camera' (since you want to scale the ball based on distance from the camera. Doesn't have to be accurate, just get the idea across)
Keep a vector for the 'direction of the force' you are going to apply to the ball. (this can be multiplied with the force from your force meter)
Keep a vector for the 'velocity of the ball'
Keep a vector for the 'gravity'
Your 'throw' function would become something along the lines of:
ball.velocity = throw.direction * throw.power
setInterval(tick,50);
Basicly, your 'tick' function (the function you apply every x-time)
ball.velocity += gravity; // we apply gravity to the speed of the ball. Pulling it down
ball.position = ball.position + ball.velocity // we add the velocity to the position every tick
if (ball.position.y < ball.radius) // if the ball is below its radius, it is colliding with the ground
{
ball.position.y = 0 - ball.position.y; // just invert its 'up' position, to make it not collide with the ground anymore
// to make the ball go back up again, invert its 'up' velocity. Gravity will get it down eventually
// dampening applied so it will bounce up less every time. For instance make that 0.9.
ball.velocity.y = 0 - (ball.velocity.y * dampening);
}
// the scale of the ball is not determined by its height, but by its distance from the camera
distanceFromCamera = (camera.position - ball.position).length()
ball.scale = 100 - (distanceFromCamera / scaleFactor);
// to make a simply guess if we are hitting the target, just check the distance to it.
distanceFromTarget = (cup.target.position - ball.position).length()
if (distanceFromTarget <= cup.target.radius) // if we 'hit the target'
handleHit()
I'm trying my hands at some simple game programming in Javascript and have come to realize I need to change the way I handle sprites. The only question is, "how"?
I have a hero that moves around with the arrow keys and fires laser rays with WASD. This is how I define rays:
function Ray (x, y, width, height, direction, index) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.direction = direction;
this.index = index;
this.speed = 512;
this.disabled = false;
}
The index just indicates where in an array of rays (heh) it is being stored. I currently have a hard-coded limit of 5 simultaneous rays, although the other restrictions (screen size, ray size, speed, hero size etc) shouldn't allow for more than 4:
var rays = [];
var numberOfRays = 0;
var rayLimit = 5;
var shotClock = 300;
And so, in the update() function that gets called by the game loop, I have listeners for the WASD keys. They look like this:
// D
if (68 in keysDown && numberOfRays <= rayLimit && Date.now() - lastShootTime > shotClock) {
lastShootTime = Date.now();
var newRayIndex = findFreeRay();
rays[newRayIndex] = new Ray(hero.x + hero.width + 12, hero.y + hero.height / 2, rayImage.width, rayImage.height, 'right', newRayIndex);
numberOfRays++;
}
(findFreeRay() just returns the lowest unused or disabled (off the screen) index in rays[])
Earlier in the update() method (I have also tried putting it later) I have the logic for updating ray movement:
rays.forEach(function(ray) {
if (ray != null && !ray.disabled) {
switch(ray.direction) {
case 'right':
ray.x += ray.speed * modifier;
break;
case 'left':
ray.x -= ray.speed * modifier;
break;
case 'up':
ray.y -= ray.speed * modifier;
break;
case 'down':
ray.y += ray.speed * modifier;
break;
}
}
});
Finally, there is the image for the ray (actually, one for horizontal rays and another one for vertical rays). Currently, I am using one Image object of each globally, that the existing rays share. But I have also tried, without much luck, to create individual image objects for every ray.
// Ray images
var rayReady = false;
var rayImage = new Image();
rayImage.onload = function() {
rayReady = true;
};
rayImage.src = "images/ray.png";
var rayVertReady = false;
var rayVertImage = new Image();
rayVertImage.onload = function() {
rayVertReady = true;
};
rayVertImage.src = "images/ray_vert.png";
And here is how they get drawn:
if (rayReady && rayVertReady && numberOfRays > 0) {
rays.forEach(function(ray) {
if (ray.x > canvas.width
|| ray.x + ray.width < 0
|| ray.y > canvas.height
|| ray.y + ray.height < 0) {
numberOfRays--;
ray.disabled = true;
}
else if (ray.direction == 'right' || ray.direction == 'left'){
ctx.drawImage(rayImage, ray.x, ray.y);
}
else {
ctx.drawImage(rayVertImage, ray.x, ray.y);
}
});
}
The problem
After shooting only a few rays, new ones start to either flicker and disappear, or stay invisible altogether. They actually exist as gameplay objects though, as they can hit targets.
What likely causes this flickering?
(credit to Matt Hackett for the base of this code)
fiddle: http://jsfiddle.net/Vr3MW/
I've been struggling with the bullet code for a while now. I know there is most likely a simpler solution (in fact I recall something similar I've done before, but I just can't remember it). But anyway;
PROBLEM: My bullets fire in all 4 directions, as they should. However if I choose to move after firing, in a different direction, the direction, and position, of the bullets will also change.
For example, if I am facing up and fire 3 bullets, then travel to the right, the bullets will change direction and velocity with me, instead I want it to continue travelling in that direction as I move in a different direction.
Here is the code for the bullets;
function Bullet(w, h, s){
this.x = tank.x;
this.y = tank.y
this.width = w;
this.height = h;
this.speed = s;
}
//setInterval of firing
function setFire(){
tankCanFire = true;
}
//Create a new bullet
function fireBullet(){
if(tankCanFire){
tankCanFire = false;
bullets.push(new Bullet(5, 10, 15));
ctx.fillText("Ding", 300, 300);
}
}
//DRAW the Bullets + velocities.
function drawBullet(){
ctx.fillText("length = " +bullets.length, 200, 200);
if (bullets.length > 0){
for (var key in bullets)
{
ctx.fillText("DONG", 200, 250);
if (tank.up == true)
{
ctx.drawImage(bulletImage, bullets[key].x + 13, bullets[key].y - 8);
bullets[key].y -= 15;
if (bullets[key].y < 0)
delete bullets[key];
}
else if (tank.down == true)
{
ctx.drawImage(bulletImage, bullets[key].x + 13, bullets[key].y + 40);
bullets[key].y += 15;
if (bullets[key].y > 400)
delete bullets[key];
}
else if (tank.left == true)
{
ctx.drawImage(bulletImage, bullets[key].x - 8, bullets[key].y + 13);
bullets[key].x -= 15;
if (bullets[key].x < 0)
delete bullets[key];
}
else if (tank.right == true)
{
ctx.drawImage(bulletImage, bullets[key].x + 40, bullets[key].y + 13);
bullets[key].x += 15;
if (bullets[key].x > 600)
delete bullets[key];
}
}
}
}
In Bullet, in addition to x,y,width,height,speed you also need to store the direction this particular bullet is going. The current code will look at the tank's orientation when moving the bullet, so the bullet's direction will change if you turn the tank. This can be fun too, but if you want traditional bullets it makes sense to store, in the bullet, the direction it's going.
The easiest way to do this is to store, not the speed, but the velocity. Something like this:
function Bullet(w, h, dx, dy){
this.x = tank.x;
this.y = tank.y
this.width = w;
this.height = h;
this.dx = dx; // how much x changes at every time step
this.dy = dy;
}