I'm pretty close to finish this program using Canvas. This program is simply a ball that falls down from top to bottom and there's a basket that catches it, that is it. However, I have the following issues.
1) When I press the left or right arrows from keyboard for more than couple of times somehow the basket will go all the way to either left or right and disappear.
2) When the ball hits the basket nothing happens (my Collision detection function doesn't work properly). However, I should say that my collision detection works just fine when the balls hits the ground (alert message shows up saying "Ball hit the ground").
Is there a way to show a message on the top of the canvas like "1 point" every time the basket catches a ball ( if there are 5 balls then I should get a message to say "5 points")
Can someone tell me what I am doing wrong please? Thank you so much in advance!!
LIVE CODE HERE
http://codepen.io/HenryGranados/pen/QNOZRa
Here's my code :
//create the constructor for the class pad
function Pad() {
//initialisation code will go here
//create private variables for the x and y coordinates
var x = 200,
y = 200,
vx = 0,
vy = 0,
padX = (canvas.width - 20) / 2;
rightPressed = false,
leftPressed = false;
//create the draw function to give us the draw method
//it accepts one parameter which is the context from the canvas it is drawn on
Pad.prototype.draw = function (context) {
//save the state of the drawing context before we change it
context.save();
//set the coordinates of the drawing area of the new shape to x and y
context.translate(x, y);
//start the line (path)
context.beginPath();
context.fillStyle = "#800000"; // This is the basket
context.moveTo(15, 20);
context.bezierCurveTo(20, 100, 150, 100, 150, 20);
//close the path
context.closePath();
context.fill();
//go ahead and draw the line
context.stroke();
//restore the state of the context to what it was before our drawing
context.restore();
}
//create a public property called X (note caps!)
Object.defineProperty(this, 'X',
{
//getter
get: function () {
//return the value of x (lower case)
return x;
},
//setter
set: function (value) {
//ste the value of x (lower case)
x = value;
}
}
)
//create a public property called Y (note caps!)
Object.defineProperty(this, 'Y',
{
//getter
get: function () {
//return the value of y (lower case)
return y;
},
//setter
set: function (value) {
//ste the value of y (lower case)
y = value;
}
}
)
padX = function () {
if (rightPressed && padX < canvas.width - 20) {
padX += 5;
}
else if (leftPressed && padX > 0) {
padX -= 5;
}
}
Pad.prototype.move = function () {
//change the x axis by the x velocity
x += vx;
//change the y axis by the y velocity
y += vy;
}
Pad.prototype.setVector = function (vector) {
//set the vx value based on this vector
vx = vector.VX;
//set the vy value based on this vector
vy = vector.VY;
}
//public method to set the vector of the saucer
Pad.prototype.accelerate = function (Acceleration) {
//set vx
vx += Acceleration.AX;
////set vy
//vy += Acceleration.AY;
}
//create a public property called Top
Object.defineProperty(this, 'Top',
{
//getter
get: function () {
//return the y posn less the height
return y - 10;
}
}
)
//create a public property called Bottom
Object.defineProperty(this, 'Bottom',
{
//getter
get: function () {
//return the y posn plus the height
return y + 10;
}
}
)
//create a public property called Left
Object.defineProperty(this, 'Left',
{
//getter
get: function () {
//return the x posn less the width
return x - 80;
}
}
)
//create a public property called Right
Object.defineProperty(this, 'Right',
{
//getter
get: function () {
//return the x posn plus the width
return x + 80;
}
}
)
}
(1) There are at least two options to solve this problem
in your Pad.move function you could limit the change of x. You change it only when its within canvas width:
Pad.prototype.move = function() {
//change the x axis by the x velocity
var canvasWidth = 400,
padWidth = 150;
if (x + vx < canvasWidth - padWidth && x + vx >= 0)
x += vx;
//change the y axis by the y velocity
y += vy;
}
or similarly as you create ground you could create walls on both sides and collide pad with them.
(2) There is no collision handling between ball and pad:
place it in function drawFrame():
if (collision.Overlapping(ball, pad)) {
context.strokeText('ball hit pad!',20,100)
//..do some other stuff here
}
(3)Which brings us to showing message on canvas, you can just draw text on canvas
var ctx = canvas.getContext("2d");
ctx.font = "30px Arial";
ctx.fillText("Hello World",10,50);
Demo: http://codepen.io/anon/pen/RaxwLp?editors=1011
Pad was blocked because when key is pressed acceleration is always increased, so in order to move in opposite direction first it must go to 0 which takes quite some time. I have added keyup event and when key is released acceleration is zeroed:
if(leftPressed){
acceleraton.HThrust(.01);
}else if(rightPressed){
acceleraton.HThrust(-.01);
}else{
acceleraton.Halt();
}
Related
I'm still pretty new to this, so I don't know how to create a collider. My end goal is to have a game like the chrome dinosaur game. Same principles, and all. My question is, though, how do I even make a collider. I will be using a .gif for the "dinosaur". I'd like to make it where if this collider were to touch another collider, the game stops and a "game over" is shown. I have tried to create a collider, but they just keep showing up underneath the screen where the game is shown. Ant tips, tricks, or advice? Thanks
Code is as follows:
let img; //background
var bgImg; //also the background
var x1 = 0;
var x2;
var scrollSpeed = 4; //how fast background is
let music; //for music
let catBus; //catbus
//collider variables
let tinyToto;
let tiniestToto;
let hin;
let totoWithBag;
let noFace;
let happySoot;
var mode; //determines whether the game has started
let gravity = 0.2; //jumping forces
let velocity = 0.1;
let upForce = 7;
let startY = 730; //where cat bus jumps from
let startX = 70;
let totoX = 900;
let totoY = 70;
let tinToX = 900;
let tinToY = 70;
var font1; //custom fonts
var font2;
p5.disableFriendlyErrors = true; //avoids errors
function preload() {
bgImg = loadImage("backgwound.png"); //importing background
music = loadSound("catbus theme song.mp3"); //importing music
font1 = loadFont("Big Font.TTF");
font2 = loadFont("Smaller Font.ttf");
//tinyToto.setCollider("rectangle",0,25,75,75)
}
function setup() {
createCanvas(1000, 1000); //canvas size
img = loadImage("backgwound.png"); //background in
x2 = width;
music.loop(); //loops the music
catBus = {
//coordinates for catbus
x: startX,
y: startY,
};
/*
tinyToto = {
x: totoX,
y: totoY,
}
tinTo = {
x : tinToX,
y: tinToY,
}
*/
catGif = createImg("catgif.gif"); //creates catbus
catGif.position(catBus.x, catBus.y); //creates position
catGif.size(270, 100); //creates how big
/*
tinyToto = createImg("TinyToto.gif")
tinyToto.position(tinyToto.x, tinyToto.y)
tinyToto.size(270,100)
tiniestTo = createImg("tiniest Toto.gif")
tiniestTo.position(tinToX.x, tinToY.y)
tiniestTo.size(270,100)
*/
mode = 0; //game start
textSize(50); //text size
}
function draw() {
let time = frameCount; //start background loop
image(img, 0 - time, 0);
image(bgImg, x1, 2, width, height);
image(bgImg, x2, 2, width, height);
x1 -= scrollSpeed;
x2 -= scrollSpeed;
if (x1 <= -width) {
x1 = width;
}
if (x2 <= -width) {
x2 = width;
} //end background loop
fill(128 + sin(frameCount * 0.05) * 128); //text colour
if (mode == 0) {
textSize(20);
textFont(font1);
text("press SPACE to start the game!", 240, 500); //what text to type
}
fill("white");
if (mode == 0) {
textSize(35);
textFont(font2);
text("CATBUS BIZZARE ADVENTURE", 90, 450); //what text to type
}
catBus.y = catBus.y + velocity; //code for jumping
velocity = velocity + gravity;
if (catBus.y > startY) {
velocity = 0;
catBus.y = startY;
}
catGif.position(catBus.x, catBus.y);
//setCollider("tinyToto")
}
function keyPressed() {
if (keyCode === 32 && velocity == 0) {
//spacebar code
mode = 1;
velocity += -upForce;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
well, this is how I would generally do that kind of thingy:
function draw(){
for(let i in objects) // objects would be cactuses or birds
if(objects[i].x > player.x &&
objects[i].x < player.x + player.width &&
objects[i].y > player.y &&
objects[i].y < player.y + player.height){
noLoop()
// maybe do something else here
} // you could also use: for(let object of objects)
}
or if you want to do class stuff:
let player = new Player()
class Entity {
hasCollided_pointRect(_x, _y, _width, _height){
if(this.x > _x &&
this.x < _x + _width &&
this.y > _y &&
this.y < _y + _height){
return true
}
}
}
class Cactus extends Entity {
update(){
if(hasCollided_pointRect(player.x, player.y, player.width, player.height))
lossEvent()
}
}
class Player {
// ...
}
function lossEvent(){
noLoop()
}
this is a pretty classy way to do it and for a small game you really don't need all of this
also MDN has a nice article on rect with rect & point with rect collisions,
point with point collision is just (x == x && y == y)
https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection
this is one of my recent loss "functions":
if(flag.health <= 0){
noLoop()
newSplashText("You lost!\nPress F5 to restart!", "center", "center", 1)
}
The way I handled game states in my Processing games was by making seperate classes for them. Then my main sketch's draw function looked something like
fun draw()
{
currentState.draw();
}
Each gamestate then acted as their own sketches (for example a menu screen, playing, game over, etc), and had a reference to the main sketch which created the states. They would then alter the main's currentState to, i.e., a new GameOverState() etc. where needed.
For now, don't worry about doing that too much if all you want a really simple gameoverscreen with an image and some text.
I would suggest a structure like this instead. Use this pseudocode in your main draw function:
fun draw()
{
if (gameOver)
{
// show game over screen
img(gameOver);
text("game over!");
// skip rest of the function
return;
}
// normal game code goes here
foo();
bar();
// update game over after this frame's game code completes
gameOver = checkGameOver();
}
Now you need a way of checking for a collision to determine the result of checkGameOver()
For the collision handling, check out Jeffrey Thompson's book/website on collision handling. It's an amazing resource, I highly recommend you check it out.
From the website I just linked, here's an excerpt from the website talking about handling collisions between 2d rectangles.
And here's a modified version of the collision handling function listed there (I updated the variable names to be a little more intuitive)
boolean rectRect(float rect1X, float rect1Y, float rect1Width, float rect1Height, float rect2X, float rect2Y, float rect2Width, float r2h)
{
// are the sides of one rectangle touching the other?
if (rect1X + rect1Width >= rect2X && // r1 right edge past r2 left
rect1X <= rect2X + rect2Width && // r1 left edge past r2 right
rect1Y + rect1Height >= rect2Y && // r1 top edge past r2 bottom
rect1Y <= rect2Y + r2h)
{ // r1 bottom edge past r2 top
return true;
}
return false;
You can use that function in your checkGameOver() function which would return a bool depending on whether your collision criteria are met.
For your game, you would loop over every obstacle in your game and check whether the dino and the obstacle overlap.
Pseudocode:
boolean checkGameOver()
{
foreach (Obstacle obstacle in obstacles)
{
if (rectRect(dino, obstacle))
{
return true;
}
}
return false;
}
I am working on creating a game that has to do with objects(circles) falling from the top of the canvas. I have these circles randomly generating at x coordinates and then falling at a constant rate. I am trying to write a collision algorithm but cannot seem to access the x coordinates or width/height of the circles that are falling.
This is how I created the circles and put them in an array. var projectiles = [] was declared at the top of my code already
function spawnEnemies()
{
var g1 = new createjs.Graphics();
g1.beginStroke('white').beginFill('red').drawCircle(Math.floor(Math.random() * 650) + 50, 50, 20);
var e = new createjs.Shape(g1);
projectiles.push(e);
stage.addChild(e);
}
This is my collision algorithm where I am trying to access the x and y coordinates of the circles and also retrieve their width and height. I used console.log to check and see what values are being returned. For p.x the value 0 is returned every time and p.width returns NaN. I am confused why p.x and p.width are not working.
function checkCollision()
{
for (i = 0; i < projectiles.length; i++) {
var p = projectiles[i];
if((p.x + width) < ship.x)
{
hit = false;
}
else if(p.x > (ship.x + ship.image.width))
{
hit = false;
}
else if(p.y > (ship.y + ship.image.height))
{
hit = false;
}
else if((p.y + p.height) < ship.y)
{
hit = false;
}
else
{
hit = true;
console.log(p.x);
}
}
The x and y properties of a Shape can be used to translate it. In your code, you instead leave them as the default (0) and draw the circle at a specific location in the Shape's canvas.
If you want the x to reflect the location of the circle, consider changing spawnEnemies to always draw the circle at the Shape's origin, then setting its x to the desired location.
g1.(...).drawCircle(0, 50, 20);
var e = new createjs.Shape(g1);
e.x = Math.(...);
I'm new in JavaScript and I'm creating a kind of shooting game. I'm to make 1 object to move toward another object. So the "bullet" get the location of the "prey" and it will move toward it. I have no idea how to implement that, I can't find anything similar on the internet. So, I tried at first something simpler:
In the following code the "bullet" move to the left. How can I specify to move it toward an object?
I have 2 object. It's the enemyBullet object(not the bullet object) which should go toward another object.
PS: English is not my native language. Sorry for any confusion
thanks.
this.draw = function () {
this.context.clearRect(this.x + 2, this.y + 1.5, this.width - 4.5, this.height);
//x+ the speed make it go to the left
this.x += this.speed;
if (self === "bullet" && this.x >= this.canvasWidth) {
return true;
}
else if (self === "enemyBullet" && this.x >= 1000) {
console.log(this.x);
return true;
}
else {
if (self === "bullet") {
this.context.drawImage(imageRepository.bullet, this.x, this.y);
}
else if (self === "enemyBullet") {
this.context.drawImage(imageRepository.enemyBullet, this.x, this.y);
}
return false;
}
};
Normalised vector
You need to find the normalised vector from one object to the next. A vector is just an arrow that has a direction and a length. In this case we normalise the length, that is make it equal to 1 unit long. We do this so we can easily set a speed when using the vector.
Function to return a vector from one point to the next
// Code is in ES6
// fx, fy is from coordinate
// tx, ty is to coordinate
function getNormVec(fx, fy, tx, ty){
var x = tx - fx; // get differance
var y = ty - fy;
var dist = Math.sqrt(x * x + y * y); // get the distance.
x /= dist; // normalised difference
y /= dist;
return {x,y};
}
Once you have the vector you can move an object by adding the vector times the speed. Example of creating and moving a bullet from myObj to myTarget
var myObj = {}
var myTarget = {};
var myBullet = {}
myObj.x = 100;
myObj.y = 100;
myTarget.x = 1000
myTarget.y = 1000
var vecToTag = getNormVect(myObj.x, myObj.y, myTarget.x, myTarget.y);
myBullet.nx = vecToTag.x; // set bullets direction vector
myBullet.ny = vecToTag.y;
myBullet.x = myObj.x; // set the bullet start position.
myBullet.y = myObj.y;
myBullet.speed = 5; // set speed 5 pixels per frame
To move the bullet
myBullet.x += myBullet.nx * myBullet.speed;
myBullet.y += myBullet.ny * myBullet.speed;
Into this simple code I use an eventListener which doesn't look to work at all. The canvas display an image and the given hitpaint() function is supposed determines whether a click occurs. I cant understand why the eventListener behaves like that. Any insight would be helpful.
mycanv.addEventListener("click", function(e) {
var output = document.getElementByID("output");
ctx.fillStyle = 'blue';
//ctx.clearRect(0,0,100,20);
if (hitpaint) {
//ctx.fillText("hit",100,20);
output.innerHTML = "hit";
} else {
//ctx.fillText("miss",100,20);
output.innerHTML = "miss";
}
}, false);
The hitpaint() function is defined as:
function hitpaint(mouse_event) {
var bounding_box = mycanv.getBoundingClientRect();
var mousex = (mouse_event.clientX - bounding_box.left) *
(mycanv.width / bounding_box.width);
var mousey = (mouse_event.clientY - bounding_box.top) *
(mycanv.height / bounding_box.height);
var pixels = ctx.getImageData(mousex, mousey, 1, 1);
for (var i = 3; i < pixels.data.length; i += 4) {
// If we find a non-zero alpha we can just stop and return
// "true" - the click was on a part of the canvas that's
// got colour on it.
if (pixels.data[i] !== 0) return true;
}
// The function will only get here if none of the pixels matched in
return false;
}
Finally, the main loop which display the picture in random location into the canvas:
function start() {
// main game function, called on page load
setInterval(function() {
ctx.clearRect(cat_x, cat_y, 100, 100);
cat_x = Math.random() * mycanv.width - 20;
cat_y = Math.random() * mycanv.height - 20;
draw_katy(cat_x, cat_y);
}, 1000);
}
There are a some issues here:
As Grundy points out in the comment, the hitpaint is never called; right now it checks for it's existence and will always return true
The mouse coordinates risk ending up as fractional values which is no-go with getImageData
Scaling the mouse coordinates is usually not necessary. Canvas should preferably have a fixed size without an additional CSS size
Add boundary check for x/y to make sure they are inside canvas bitmap
I would suggest this rewrite:
mycanv.addEventListener("click", function(e) {
var output = document.getElementByID("output");
ctx.fillStyle = 'blue';
//ctx.clearRect(0,0,100,20);
if (hitpaint(e)) { // here, call hitpaint()
//ctx.fillText("hit",100,20);
output.innerHTML = "hit";
} else {
//ctx.fillText("miss",100,20);
output.innerHTML = "miss";
}
}, false);
Then in hitpaint:
function hitpaint(mouse_event) {
var bounding_box = mycanv.getBoundingClientRect();
var x = ((mouse_event.clientX - bounding_box.left) *
(mycanv.width / bounding_box.width))|0; // |0 cuts off any fraction
var y = ((mouse_event.clientY - bounding_box.top) *
(mycanv.height / bounding_box.height))|0;
if (x >= 0 && x < mycanv.width && y >= 0 && y < mycanv.height) {
// as we only have one pixel, we can address alpha channel directly
return ctx.getImageData(x, y, 1, 1).data[3] !== 0;
}
else return false; // x/y out of range
}
I have a color picker that appears only when i click on another gui texture button. also i have an object that has a rotating script behind it. how can i tell the object to not rotate when i am using the color picker ?
This is the rotating script.
function LateUpdate () {
if (isCameraInputIgnored() ) {
return;
}
if (target && Input.GetMouseButton(0)) {
x += Input.GetAxis("Mouse X") * xSpeed * 0.02;
y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02;
y = ClampAngle(y, yMinLimit, yMaxLimit);
var rotation = Quaternion.Euler(y, x, 0);
var position = rotation * Vector3(0.0, 10.0, -distance) + target.position;
transform.rotation = rotation;
transform.position = position;
}
}
This function returns true if the mouse is over the 2 always present guitextures.
function isCameraInputIgnored () : boolean {
var mousePos : Vector3 = Input.mousePosition;
// invert the y-coordinate
mousePos.y = Screen.height - mousePos.y;
if ( tilesBlock.Contains(mousePos) || wallBlock.Contains(mousePos))
return true;
return false; //if mouse isn't over gui's
}
Declaration :
private var tilesBlock : Rect = Rect(0,0,570,182) ; //tiles rectangle
private var wallBlock : Rect = Rect(670,0,590,172) ; //walls rectangle
In the LateUpdate functon i use this code to not rotate my object :
if (isCameraInputIgnored() ) {
return;
}
My problem is that after one of the second gui texture buttons is pressed a color picker appear that is declares like a GuiLayout.BeginArea(Rect) and when i drag my mouse through color my house is movng and changing colors at the same time. I want my house fixed when i am using the color picker.