Hello first time asking a question and I would appreciate any help as I code just for fun. I'm making a simple game in canvas and need help to adjust the hitbox on two objects. I read several other threads that touch on the subject but couldn't find one that answers my question.
Example of what is happening
As seen from the image the hitbox of the smiley face extends beyond the actual image that is drawn. The actual image in the game will be more complex than a smiley face. However using the bounding box method does not account for what the user does not see so on screen, as the images do not look like they are colliding but in reality they are. I thought of trying to clip the image by drawing a triangle within the hitbox and then doing some calculations based on the area. This is what I came up with so far.
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var player = {
x: 0,
y: 0,
width: 100,
height: 100
}
var obstacle = {
x: 150,
y: 100,
width: 100,
height: 100
};
document.addEventListener("keydown", keyDownHandler);
function keyDownHandler(e) {
ctx.clearRect(0,0,canvas.width,canvas.height);
if(e.keyCode === 37) {
player.x -= 10;
}
else if(e.keyCode === 38) {
player.y -= 10;
}
else if(e.keyCode === 39) {
player.x += 10;
}
else if(e.keyCode === 40) {
player.y += 10;
}
function drawTriangle(){
ctx.beginPath();
ctx.moveTo(player.x+player.width,player.y+player.height-40);
ctx.lineTo(player.x+player.width-40,player.y+player.height);
ctx.lineTo(player.x+player.width,player.y+player.height);
ctx.closePath();
ctx.fillStyle="orange";
ctx.fill();
}
function drawObstacle(){
ctx.beginPath();
ctx.fillRect(obstacle.x,obstacle.y,obstacle.width,obstacle.height);
ctx.closePath();
}
function colorChange(){
var color = "black";
var triArea = (player.y+player.height-40)*(player.x+player.width-40)/2;
if(player.x+player.width >= obstacle.x &&
player.x <= obstacle.x+obstacle.width &&
player.y <= obstacle.y+obstacle.height &&
player.y+player.height >= obstacle.y){
if(triArea <= 5500 && triArea != 5250){
color = "red";
}
else {
color = "green";
}
document.getElementById("playerArea").innerHTML = ((player.x+player.width)*(player.y+player.height));
document.getElementById("triangleArea").innerHTML = triArea;
}
else{
color = "black";
}
ctx.fillStyle = color;
}
ctx.strokeRect(player.x,player.y,player.width,player.height);
colorChange();
drawObstacle();
ctx.closePath();
drawTriangle();
}
https://codepen.io/brucelin82/pen/aqQLxW?editors=1010
The obstacle will change to red if a collision has occurred but not past the triangle. Once the collision extends beyond the triangle the obstacle will turn to green. The code is still a work in progress but I was just wondering if I'm on the right track or if there is an easier method to account for the empty space within the image. Thank you in advance for any direction you can give me.
Related
I begin to animate a blue square in HTML5 inside a canvas tag, I add another red square as enemy; but when I move the blue square, the red one disappear and the blue one turn red. In fact, I want to keep the color of the blue square and I don't want the red one disappear, how can I solve this problem?
var x = 10;
var y = 360;
var xEnnemy = 190;
var yEnnemy = 10;
var v = 4;
var width = 400;
var height = 400;
var can = document.getElementById("can");
var ctx = can.getContext("2d");
ctx.fillStyle = "skyblue";
ctx.fillRect(x, y, 40, 40);
ctx.fillStyle = "red";
ctx.fillRect(xEnnemy, yEnnemy, 20, 20);
window.onkeydown = function direc(e) {
//Move left
if (e.keyCode == 37 && x > 0) {
x -= v;
}
//Move up
if (e.keyCode == 38 && y > 0) {
y -= v;
}
//Move right
if (e.keyCode == 39 && x < (width - 40)) {
x += v;
}
//Move down
if (e.keyCode == 40 && y < (height - 40)) {
y += v;
}
ctx.clearRect(0, 0, width, height);
ctx.fillRect(x, y, 40, 40);
}
function ennemy() {
ctx.fillStyle = "red";
ctx.fillRect(xEnnemy, yEnnemy, 40, 40);
}
#can {
border: 1px solid black;
}
<html>
<body>
<canvas id="can" width="400" height="400"></canvas>
</body>
</html>
Using ctx.clearRect(0, 0, width, height); clears the entire scene so you need to redraw everything.
You probably want to define a drawPlayer function similar to ennemy like:
function drawPlayer(){
ctx.fillStyle = "skyblue";
ctx.fillRect(x, y, 40, 40);
}
Then, after updating the game state you'd call:
ctx.clearRect(0, 0, width, height);
drawPlayer();
ennemy();
This will update the canvas with the new positions and with the appropriate colors.
Look at what you are doing in the: function direc(e) {
ctx.clearRect(0, 0, width, height);
ctx.fillRect(x, y, 40, 40);
That is effectively wiping the canvas and only drawing one rectangle...
Here is what I would do
var player = {x:10, y:100, v: 4}
var enemy = {x:190, y:10};
var can = document.getElementById("can");
var ctx = can.getContext("2d");
window.onkeydown = function direc(e) {
if (e.keyCode == 37 && player.x > 0)
player.x -= player.v; // Move left
if (e.keyCode == 38 && player.y > 0)
player.y -= player.v; // Move up
if (e.keyCode == 39 && player.x < (can.width - 40))
player.x += player.v; // Move right
if (e.keyCode == 40 && player.y < (can.height - 40))
player.y += player.v; // Move down
draw()
}
function draw() {
ctx.clearRect(0, 0, can.width, can.height);
ctx.fillStyle = "skyblue";
ctx.fillRect(player.x, player.y, 40, 40);
ctx.fillStyle = "red";
ctx.fillRect(enemy.x, enemy.y , 20, 20);
}
draw()
#can {
border: 1px solid black;
}
<canvas id="can" width="400" height="160"></canvas>
I consolidated all the drawing in one location function draw() any time we need to draw we can call that, at the moment you only have one event: onkeydown but as your game gets more complex very likely you will have more and they all will require drawing, we just call the same function.
Also I introduced objects player = {x:10, y:100, v: 4} that keep all the properties of the player in one object, that makes the code easier to read
I have created a horizontal line in HTML5 Canvas. I want to animate the line to move infinity up and down. Is it possible?
function horizontal_line() {
context.beginPath();
context.moveTo(100, 100);
context.lineTo(5000, 100);
context.strokeStyle = "Green";
context.stroke();
}
For an animation you will need to have a way to draw different frames, and in each frame you have to delete the previous one, calculate the new position of the line, and then draw the line again:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.strokeStyle = "Green";
var posY = 0;
var lineLength = 10;
var speed = 2;
function drawLine() {
ctx.beginPath();
ctx.moveTo(10, posY);
ctx.lineTo(10, posY+lineLength);
ctx.stroke();
}
function moveLine () {
posY += speed;
if (posY < 0 || posY > canvas.height) {
speed = speed * -1;
}
}
function loop() {
// clear old frame;
ctx.clearRect(0,0,canvas.width, canvas.height);
moveLine();
drawLine();
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
<canvas id="canvas"></canvas>
In this example requestAnimationFrame is what provides you the frames you need, so the function loop() gets called over and over again. In there we clear the old frame with clearRect(), calculate the new position, and then we draw the new Line with drawLine().
So I would like to add velocity to my swipe gesture. Currently I have it moving 10 px on swipe, but I want it to move with a velocity so the user can toss the object around on the screen. Here's the code I am currently using with the object moving the 10 px.
function onSwipe (e:TransformGestureEvent):void{
player_mc.gotoAndStop(5);
if (e.offsetX == 1) {
//User swiped towards right
player_mc.x += 10;
}
if (e.offsetX == -1) {
//User swiped towards left
player_mc.x -= 10;
}
if (e.offsetY == 1) {
//User swiped towards bottom
player_mc.y += 10;
}
if (e.offsetY == -1) {
//User swiped towards top
player_mc.y -= 10;
}
if(collision(player_mc.cp, level_mc))
{
player_mc.x = player_mc.prevX;
player_mc.y = player_mc.prevY;
}
}
Thanks for the help
Here is a way you can do this: (you'll want to swap the mouse events for touch events if using on mobile)
player_mc.addEventListener(MouseEvent.MOUSE_DOWN,startSwipe);
player_mc.addEventListener(MouseEvent.MOUSE_UP,endSwipe);
var velocity:Point = new Point(); //this stores the current velocity of the object
var previous:Point = new Point(); //this stores the previous frames x/y of the object.
var isDragging:Boolean = false;
var friction:Number = .85; //this how quickly to slow down the object once the mouse is released - smaller the number (must be less than 1) the quicker the object will slow/stop
function startSwipe(e){
isDragging = true;
player_mc.startDrag(false);
this.addEventListener(Event.ENTER_FRAME,enterFrameHandler);
}
function enterFrameHandler(e):void {
if(isDragging){
velocity.x += player_mc.x - previous.x; //we're adding the new velocity to the old one, then dividing in half to average the value - this makes it seem smoother
velocity.y += player_mc.y - previous.y;
velocity.x *= .5; //dividing in half
velocity.y *= .5;
previous.x = player_mc.x;
previous.y = player_mc.y;
}else{
velocity.x *= friction; //gradually slow down the object
velocity.y *= friction;
player_mc.x += velocity.x;
player_mc.y += velocity.y;
if(velocity.x < .05 && velocity.y < .05){ //once it gets slow enough, stop the enter frame handler
this.removeEventListener(Event.ENTER_FRAME,enterFrameHandler);
}
}
trace(velocity);
}
function endSwipe(e){
player_mc.stopDrag();
isDragging = false;
}
I have recently been making a game in dart. In the game, the user controls a space ship sprite with the W, A, S and D keys. These will make the sprite move UP. Left, Right and Down respectively. Space will make a square that represents a projectile, missile bullet etc. move at great speed in the direction the ship is facing.
I have a very bad system that works:
(FOR HANDLING KEY PRESS)
bool drawBull = false;
void handleKeyboard(KeyboardEvent event) {
kevent = event.keyCode;
if (kevent == KeyCode.W || kevent == KeyCode.UP){
direction = 'up';
window.console.log('w / up');
}
else if (kevent == KeyCode.A || kevent == KeyCode.LEFT){
direction = 'left';
window.console.log('a / left');
}
else if (kevent == KeyCode.S || kevent == KeyCode.DOWN){
direction = 'down';
window.console.log('s / down');
}
else if (kevent == KeyCode.D || kevent == KeyCode.RIGHT){
direction = 'right';
window.console.log('d / right');
}
else if (kevent == KeyCode.SPACE) {
shotX = lastX; shotY = lastY;
if (direction == 'right') { shotX = shotX + 400; }
if (direction == 'down') { shotY = shotY + 400; }
drawBull = true;
window.console.log('space');
} else {
return null;
}
}
And the draw function itself:
void draw() {
canvas.width = canvas.width;
switch (direction) {
case 'up':
lastY = lastY - 3;
context.drawImage(shipU, lastX, lastY);
if (drawBull) { shotY = shotY - 30; context.fillRect(lastX + 240, shotY, 20, 20); }
break;
case 'left':
lastX = lastX - 3;
context.drawImage(shipL, lastX, lastY);
if (drawBull) { shotX = shotX - 30; context.fillRect(shotX, lastY + 240, 20, 20); }
break;
case 'down':
lastY = lastY + 3;
context.drawImage(shipD, lastX, lastY);
if (drawBull) { shotY = shotY + 30; context.fillRect(lastX + 240, shotY, 20, 20); }
break;
case 'right':
lastX = lastX + 3;
context.drawImage(shipR, lastX, lastY);
if (drawBull) { shotX = shotX + 30; context.fillRect(shotX, lastY + 240, 20, 20); }
break;
default:
return null;
}
}
As you can see, this is a long untidy and tedious method. However, despite all my brain racking I can't think of a system that avoids these many if/switch statements and the idea of writing out the draw image and shooting code for each one.
My actual game will be heavily object orientated of course, so perhaps an object-orientated solution would be helpful.
The code answer given to this question was quite nice, although doesn't fit my needs exactly. So perhaps an adoption of a class like that would work well How to listen to key press repetitively in Dart for games?
Thank you very much for your help!
-Tom W.
Instead of storing directions as strings, you could store the Velocity and/or Rotation of items, and use that for rendering.
You could then store a map of the key vs the velocity change:
var keyHandlers = {
KeyCode.W: new Point(0, 1),
KeyCode.S: new Point(0, -1),
KeyCode.A: new Point(-1, 0),
KeyCode.D: new Point(1, 0),
}
var spritePosition = new Point(0, 0);
void handleKeyboard(KeyboardEvent event) {
if (keyHandlers[event.keyCode])
spritePosition += keyHandlers[event.keyCode];
}
Ultimately, you probably want to actually store a Velocity for the sprite, and use input to increase the velocity; and then "decay" that velocity each frame (to represent gravity/friction, etc.).
If your sprite needs to be rendered at a particular direction, you should store a rotation as well as position and velocity. There's a simple set of slides here that could be useful; and there are also lots of good XNA tutorials out there that will cover building a "GameObject" class to wrap up this state (although XNA is dead, the tutorials are great, and the C# is not a million miles from Dart).
When your ship shoots, you can copy the position and rotation from the ship to the bullet and calculate it's velocity by applying the ships rotation to the starting velocity of your bullet.
There's also a great website by Bob Nystrom at gameprogrammingpatterns.com that I'd recommend reading as you get more into it.
So, I'm absolutely useless at Flash and stupidly thought creating a Snooker game wouldn't be too difficult for a school Assignment. I grossly underestimated.
I'm trying to make the cueBall MovieClip move towards the point where the Mouse clicked on the Stage.
So far I have made the cueBall move to the point where the Mouse clicked, but I need it to keep going (unless obstructed).
I think I need to somehow calculate the angle between the cueBall MovieClip and the Mouse and then tell the MovieClip to start moving in that direction.
Suggestions? It's probably something simple, I bet...
Thanks in advance.
addEventListener(Event.ENTER_FRAME, gameSetup);
var VelocityX;
var VelocityY;
var speed = 1;
var shootCount = 0;
var mouseXPos;
var mouseYPos;
function gameSetup(e:Event) {
removeEventListener(Event.ENTER_FRAME, gameSetup);
addEventListener(Event.ENTER_FRAME, aim);
addEventListener(Event.ENTER_FRAME, shoot);
}
function aim(e:Event) {
cueStick.x = cueBall.x;
cueStick.y = cueBall.y;
cueStick.rotation = (Math.atan2(mouseY-cueStick.y, mouseX-cueStick.x))*(180/Math.PI);
if (mouseX > 25.5 && mouseX < 614.5) {
aimBall.visible = true;
aimBall.x = mouseX;
} else if (mouseX < 25.5) {
aimBall.x = 25.5;
} else if (mouseX > 614.5) {
aimBall.x = 614.5;
}
if (mouseY > 25.5 && mouseY < 294.5) {
aimBall.visible = true;
aimBall.y = mouseY;
} else if (mouseY < 25.5) {
aimBall.y = 25.5;
} else if (mouseY > 294.5) {
aimBall.y = 294.5;
}
if (mouseX > 0 && mouseX < 640 && mouseY > 0 && mouseY < 320) {
Mouse.hide();
} else {
Mouse.show();
}
addEventListener(MouseEvent.MOUSE_DOWN, drawCue);
}
function drawCue(e:MouseEvent) {
removeEventListener(Event.ENTER_FRAME, aim);
addEventListener(MouseEvent.MOUSE_UP, shotAnim);
}
function shotAnim(e:MouseEvent) {
mouseXPos = mouseX;
mouseYPos = mouseY;
cueStick.rotation = (Math.atan2(mouseYPos-cueStick.y, mouseXPos-cueStick.x))*(180/Math.PI);
VelocityX = Math.cos(mouseX-cueBall.x) * speed;
VelocityY = Math.sin(mouseY-cueBall.y) * speed;
cueStick.gotoAndPlay(2);
}
function shoot(e:Event) {
if (shootCount == 1) {
cueBall.x += VelocityX;
cueBall.y += VelocityY;
trace(VelocityX);
trace(VelocityY);
cueStick.visible = false;
} else {
cueStick.visible = true;
}
}
Maybe this example is helpful to you:
http://www.emanueleferonato.com/2008/01/05/complete-flash-pool-game-with-highscores/
If I'm understanding you right, you already have it setup so that when the mouse is clicked, the ball teleports to the mouse's location.
What you want is for the ball to move to the mouse's position over a period of time.
To do this, you will use something called an EnterFrame event. EnterFrame calls a function every frame, which you need to get the ball smoothly rolling to the mouse.
addEventListener(Event.ENTER_FRAME,update);
function update(event:Event) {
// move the ball a little bit toward the mouse
}
Then, you need to determine in what direction the ball needs to roll. You will do this with trigonometric functions. In your OnClick handler, write something like:
VelocityX = Math.cos(mouse.x-ball.x) * SPEED;
VelocityY = Math.sin(mouse.y-ball.y) * SPEED;
Then, each frame, move the ball by that amount.
addEventListener(Event.ENTER_FRAME,update);
function update(event:Event) {
ball.x += VelocityX;
ball.y += VelocityY;
}
Hope that helped!