Using Keyboard input for 2 objects - javascript

edit: SOLVED
I'm a highschool student in Japan trying to learn how to program.
I recently viewed https://vimeo.com/105955605 this video, and decided I could use the beginning section to start building pong in javascript.
I'm pretty much a complete novice with programming and/or javascript and I still have a long way to go.
I got Player1 (left paddle) to work on its own, so I figured I could just copy paste, mess with a couple things, and make Player2. However, now Player2 moves when I press w/s, but Player1 no longer moves.
I've tried creating 2 separate keyboarder() functions, using this.keyboarder from Player1 in player2 (Player2.keyboarder = Player1.keyboarder() ), and declaring/calling keyboarder() before doing anything else.
HTML:
<html>
<head>
<meta charset="UTF-8">
<title>Pong</title>
<link type="text/css" rel="stylesheet" href="css/stylesheet.css">
</head>
<body>
<canvas id="screen" width="310" height="210"></canvas>
<script src="js/pong.js"></script>
</body>
</html>
JavaScript:
;(function(){
//Main game function
//tells objects in bodies array to update.
//stores gameSize pulled from canvasId
var Game = function(canvasId){
var canvas = document.getElementById(canvasId);
var screen = canvas.getContext('2d');
var gameSize = {x: canvas.width, y: canvas.height};
var self = this;
//bodies array
this.bodies = [new Player1(this, gameSize), new Player2(this, gameSize)];
//update function
var tick = function(){
self.update();
self.draw(screen,gameSize);
requestAnimationFrame(tick);
};
tick();
};
//constructer for game() function. tells bodies to update, and draw
Game.prototype = {
update: function(){
for(var i =0 ; i < this.bodies.length; i++){
this.bodies[i].update();
}
},
draw:function(screen,gameSize){
screen.clearRect(0,0,gameSize.x,gameSize.y);
for(var i =0 ; i < this.bodies.length; i++){
drawRect(screen, this.bodies[i]);
}
}
};
//P1 object, declares size and start position of P1
var Player1= function(game, gameSize){
this.size = {x:30,y:gameSize.y / 3};
this.game = game;
this.gameSize = gameSize;
this.center = {x: 0, y:gameSize.y/2};
this.keyboarder = new Keyboarder();
requestAnimationFrame(this.update);
};
//constructor for P1, updates position based on keyboard input
Player1.prototype = {
update:function(){
if (this.keyboarder.isDown(this.keyboarder.KEYS.DOWN) && this.center.y < (5*this.gameSize.y / 6)){
this.center.y += 4;
}else if(this.keyboarder.isDown(this.keyboarder.KEYS.UP) && this.center.y > this.size.y /2 ){
this.center.y -= 4;
}
}
};
//P2, same as P1 aside from position
var Player2= function(game, gameSize){
this.size = {x:30,y:gameSize.y / 3};
this.game = game;
this.gameSize = gameSize;
this.center = {x: gameSize.x, y:gameSize.y/2};
this.keyboarder = new Keyboarder();
requestAnimationFrame(this.update);
};
//constructor for P2, same as P1
Player2.prototype = {
update:function(){
if (this.keyboarder.isDown(this.keyboarder.KEYS.S) && this.center.y < (5*this.gameSize.y / 6)){
this.center.y += 4;
}else if(this.keyboarder.isDown(this.keyboarder.KEYS.W) && this.center.y > this.size.y /2 ){
this.center.y -= 4;
}
}
};
//Draw function, draws object
var drawRect = function(screen, body){
screen.fillRect(body.center.x - body.size.x /2,
body.center.y - body.size.y /2, body.size.x,body.size.y);
};
//Keyboard input function
//reads if keys are being pressed and takes the event code
//isDown() returns boolean of key down = true, key up = false
var Keyboarder = function(
){
var keyState = {};
window.onkeydown = function(e){
keyState[e.keyCode] = true;
};
window.onkeyup = function(e){
keyState[e.keyCode] = false;
};
this.KEYS = {DOWN: 37, UP:39,W:87 , S: 83};
this.isDown = function(keyCode){
return keyState[keyCode] === true;
};
};
//calls game() function when the page has loaded.
window.onload = function(){
new Game("screen")
};
})();
Sorry if this is bad protocol for using stackoverflow, I'm also new to asking questions here.

The problem is you have two Keyboarder instances, and they're both binding to the key events by assigning a handler directly to them - this will overwrite any other handlers. There's two ways to fix it:
1: Don't assign directly, instead use addEventListener, eg:
window.addEventListener('keydown', function(e){
keyState[e.keyCode] = true;
});
2: Use the same keyboarder instance for both players:
var kb = new Keyboarder();
this.bodies = [new Player1(this, gameSize, kb), new Player2(this, gameSize, kb)];
and in your player object assign it to this 3rd parameter. Even if you go this route, I would still advise on using addEventListener as well simply to ensure that the events can be bound to in multiple places if needed.
On another point, you should also be able to refactor your players into a single function and create two instances of it with different parameters, but that sort of thing is better dealt with over on Code Review.

When you create two Keyboarder instances, one collides with the other, because of this:
window.onkeydown = function(e){
keyState[e.keyCode] = true;
};
window.onkeyup = function(e){
keyState[e.keyCode] = false;
};
When you create one Keyboarder, the event listeners the previous one attached are overriden. Try something like this:
window.addEventListener('keydown', function(e){
keyState[e.keyCode] = true;
});
window.addEventListener('keyup', function(e){
keyState[e.keyCode] = false;
});
This ensures there are listeners for every instance of the Keyboarder.

Related

Difficulty implementing handleOnPress function

I am trying to implement the handleOnPress function, the code is provided by the textbook which is confusing because it doesn't seem to be working as expected, in fact it does nothing.
The function is supposed to allow me to click the squares in the memory game, if the squares are the same color both of the chosen squares disappear, if you click on the same square twice the function resets and if you match two that aren't the same the function resets.
It would be really appreciated if anyone can take a look and point out any errors they see, thank you!
<!DOCTYPE html>
<html>
<head>
<title>Recipe: Drawing a square</title>
<script src="easel.js"></script>
<script type="text/javascript">
var canvas;
var stage;
var squareSide = 70;
var squareOutline = 5;
var max_rgb_color_number = 255;
var gray = createjs.Graphics.getRGB(20, 20, 20);
var placementArray = [];
var highlight = createjs.Graphics.getRGB(255, 255, 0);
var tileClicked;
function init() {
var rows = 6;
var columns = 6;
var squarePadding = 10;
canvas = document.getElementById('myCanvas');
stage = new createjs.Stage(canvas);
var numberOfTiles = rows*columns;
setPlacementArray(numberOfTiles);
for(var i=0;i<numberOfTiles;i++){
var placement = getRandomPlacement(placementArray);
if(i % 2 === 0){
var color = randomColor();
}
square = drawSquare(color);
square.color = color;
square.x = (squareSide+squarePadding) * (placement%columns);
square.y = (squareSide+squarePadding) * Math.floor(placement/columns);
stage.addChild(square);
square.onPress = handleOnPress;
stage.update();
}
}
function drawSquare(color) {
var shape = new createjs.Shape();
var graphics = shape.graphics;
graphics.setStrokeStyle(squareOutline);
graphics.beginStroke(gray);
graphics.beginFill(color);
graphics.rect(squareOutline, squareOutline, squareSide, squareSide);
return shape;
}
function randomColor(){
var color = Math.floor(Math.random()*max_rgb_color_number);
var color2 = Math.floor(Math.random()*max_rgb_color_number);
var color3 = Math.floor(Math.random()*max_rgb_color_number);
return createjs.Graphics.getRGB(color, color2, color3);
}
function setPlacementArray(numberOfTiles){
for(var i=0;i<numberOfTiles;i++){
placementArray.push(i);
}
}
function getRandomPlacement(placementArray){
randomNumber = Math.floor(Math.random()*placementArray.length);
return placementArray.splice(randomNumber, 1)[0];
}
function handleOnPress(event){
var tile = event.target;
if(!!tileClicked === false){
tile.graphics.setStrokeStyle(squareOutline).beginStroke(highlight)
.rect(squareOutline, squareOutline, squareSide, squareSide);
tileClicked = tile;
}
else{
if(tileClicked.color === tile.color && tileClicked !== tile){
tileClicked.visible = false;
tile.visible = false;
}
else{
tileClicked.graphics.setStrokeStyle(squareOutline).beginStroke(gray)
.rect(squareOutline, squareOutline, squareSide, squareSide);
}
tileClicked = null;
}
stage.update();
}
</script>
</head>
<body onload="init()">
<canvas id="myCanvas" width="960" height="600"></canvas>
</body>
</html>
The onEvent-style handlers (onClick, onMouseDown, etc) were deprecated and removed a long time ago (Version 0.7.0, September 25, 2013).
Instead, use events, such as
square.addEventListener("mousedown", handleOnPress);
or the shortcut on() method:
square.on("mousedown", handleOnPress);
Note also there is no "press" event. There is a "mousedown" event, as well as "pressmove"/"pressup" events for drag and drop-style events.
Here is a listing of events on all display objects: https://createjs.com/docs/easeljs/classes/DisplayObject.html#event_mousedown
I don't know if this will solve everything, but it should get your click handlers firing.

EaselJs: Objects, Classes, and Making A Square Move

Although I have used Javascript extensively in the past, I have never used classes and objects in my programs. This is also the first for me using the HTML5 canvas element with an extra Javascript library. The library I'm using is EaselJS.
Short and sweet, I'm trying to make a square move with keyboard input, using object-oriented programming. I've already looked over sample game files, but I've never been able to properly get one to work.
The following is my classes script:
/*global createjs*/
// Shorthand createjs.Shape Variable
var Shape = createjs.Shape;
// Main Square Class
function square(name) {
this.name = name;
this.vX = 0;
this.vY = 0;
this.canMove = false;
this.vX = this.vY = 0;
this.canMove = false;
this.body = new Shape();
this.body.graphics.beginFill("#ff0000").drawRect(0, 0, 100, 100);
}
And below is my main script:
/*global createjs, document, window, square, alert*/
// Canvas and Stage Variables
var c = document.getElementById("c");
var stage = new createjs.Stage("c");
// Shorthand Create.js Variables
var Ticker = createjs.Ticker;
// Important Keycodes
var keycode_w = 87;
var keycode_a = 65;
var keycode_s = 83;
var keycode_d = 68;
var keycode_left = 37;
var keycode_right = 39;
var keycode_up = 38;
var keycode_down = 40;
var keycode_space = 32;
// Handle Key Down
window.onkeydown = handleKeyDown;
var lfHeld = false;
var rtHeld = false;
// Create Protagonist
var protagonist = new square("Mr. Blue");
// Set Up Ticker
Ticker.setFPS(60);
Ticker.addEventListener("tick", stage);
if (!Ticker.hasEventListener("tick")) {
Ticker.addEventListener("tick", tick);
}
// Init Function, Prepare Protagonist Placement
function init() {
protagonist.x = c.width / 2;
protagonist.y = c.height / 2;
stage.addChild(protagonist);
}
// Ticker Test
function tick() {
if (lfHeld) {
alert("test");
}
}
// Handle Key Down Function
function handleKeyDown(event) {
switch(event.keyCode) {
case keycode_a:
case keycode_left: lfHeld = true; return false;
case keycode_d:
case keycode_right: rtHeld = true; return false;
}
}
This is the error I get in the Developer Tools of Chrome:
Uncaught TypeError: undefined is not a function
easeljs-0.7.0.min.js:13
In case you're wondering, the order of my script tags is the EaselJS CDN, followed by my class, followed by the main script file.
I would really like closure on this question. Thank you in advance.
I figured it out. I was adding the entire protagonist instance to the stage. I've fixed by adding the protagonist.body to the stage.

Phaser 2 sprite detect border?

for college I have to create a game, my game involves falling objects. But I cannot figure out how to lose a life when the object goes out of bounds.
Heres some of my code:
//my variables
var player;
var lives;
var max_lives = 3;
var objects;
var totalLife
var lifeText;
var totalScore;
var scoreText;
var inputs;
//creates the group for the objects
objects = this.add.group();
this.physics.enable(objects, Phaser.Physics.ARCADE);
objects.setAll('outOfBoundsKill', true);
objects.setAll('checkWorldBounds', true);
//spawns object
spawnObject: function () {
var random = this.rnd.integerInRange(0, 25);
if (random === 0) {
var randomX = this.rnd.integerInRange(0, this.world.width - 150);
var object = objects.create(randomX, -50, 'object');
this.physics.enable(object, Phaser.Physics.ARCADE);
object.body.velocity.y = this.rnd.integerInRange(200, 300);
}
}
Sprites fire an event when they leave the world bounds. Listen for it to drop one life.
Enable the event being emitted using:
sprite.checkWorldBounds = true;
Then, you can listen for:
sprite.events.onOutOfBounds
...and react to it by dropping one life.

Animating canvas with a javascript constructor

Hello stackoverflow community!
First I must say that I dont have much experience with constructors.
So. What I am trying to do, is to animate a parachutist to fly from top to bottom of the screen.
I thought I could use a constructor to set up a parachutist:
var parachute = function() {
this.height = 35;
this.width = 30;
this.speed = 50;
this.xPos = Math.round(Math.random() * (window.width - this.width));
this.animate = function() {
this.img = new Image();
this.yPos = 0;
this.img.onload = function() {
ctxPara.globalCompositeOperation = 'copy';
ctxPara.translate(0, this.yPos);
ctxPara.drawImage(this.img, this.xPos, 0);
};
this.img.src = 'para.png';
this.yPos++;
};
};
This constructor is used in a function called 'fly':
var fly = function() {
var newParachute = new parachute();
setInterval(newParachute.animate, newParachute.speed);
};
And this 'fly' function is triggered when the window loads:
window.onload = function() {
var canvasBg = document.getElementById('canvasBg');
// I splitt the Background and the parachutists in two canvas elements
// handling the problem (to erase content and draw new content) with
// the canvas animation.
var canvasPara = document.getElementById('canvasPara');
ctxPara = canvasPara.getContext('2d');
canvasPara.width = window.width;
canvasPara.height = window.height;
canvasBg.width = window.width;
canvasBg.height = window.height;
fly();
clouds(); // background is loading here
};
What you should see, is a Parachutist flying down the screen. But unfortunately you don't...
Now, after that Long text. (Iam very sorry that it is so long :-( ) My question is: Do you know what I am doing wrong? Is my constuctor correct? Is, what i am trying to do, supposed to be written like this? Any advices or suggestions for a succesfull opportunity? (I hope my english isn't that terrible I think it is :-) )
Oh i forgot to mention the error. It's a TypeMissMatchError.
That means 'this.img' is not an img element at this line:
ctxPara.drawImage(this.img, this.xPos, 0);
Now, I followed the example of markE.
Instead of showing me a parachutist. It shows me an error in this line: ctxPara.drawImage(this.img, this.xPos, this.yPos);
var fly = function () {
var newParachute = new parachute();
newParachute.img.load.call(newParachute);
setInterval(newParachute.animate.call(newParachute), newParachute.speed);
};
var parachute = function () {
this.height = 35;
this.width = 30;
this.speed = 25;
this.xPos = Math.round(Math.random() * (window.innerWidth - this.width));
this.img = new Image();
this.yPos = 0;
this.img.isLoaded = false;
this.img.load = function () {
this.img.isLoaded = true;
};
this.img.src = 'parachute.png';
this.animate = function () {
if (this.img.isLoaded) {
ctxPara.clearRect(0, 0, canvasPara.width, canvasPara.height);
ctxPara.drawImage(this.img, this.xPos, this.yPos); // ERROR: 'Unknown Error'.
this.yPos++;
console.log('animating');
}
};
};
I am stuck again. But now i don't even know the reason... Please help!?
Demo: http://jsfiddle.net/m1erickson/ym55y/
A couple of issues:
(1) To get the window width you can use:
window.innerWidth
(2) setInterval calls newParachute.animate.
setInterval(newParachute.animate, newParachute.speed);
But this inside animate the window object--not the Parachute object.
To give the correct this to animate you can use the call method like this:
var newParachute = new parachute();
setInterval(function(){newParachute.animate.call(newParachute);}, newParachute.speed);
(3) You need to deal with clearing previously drawn images or they will still show on your canvas.

I'm trying to stop snow script and clear the page after x seconds

How can I make the snow clear after a certain time. I've tried using variables and the calling a timeout which switches on to false and stops the makesnow() function but that doesn't seem to clear the page at all.
<script language="javascript">
ns6 = document.getElementById;
ns = document.layers;
ie = document.all;
/*******************[AccessCSS]***********************************/
function accessCSS(layerID) { //
if(ns6){ return document.getElementById(layerID).style;} //
else if(ie){ return document.all[layerID].style; } //
else if(ns){ return document.layers[layerID]; } //
}/***********************************************************/
/**************************[move Layer]*************************************/
function move(layer,x,y) { accessCSS(layer).left=x; accessCSS(layer).top = y; }
function browserBredde() {
if (window.innerWidth) return window.innerWidth;
else if (document.body.clientWidth) return document.body.clientWidth;
else return 1024;
}
function browserHoyde() {
if (window.innerHeight) return window.innerHeight;
else if (document.body.clientHeight) return document.body.clientHeight;
else return 800;
}
function makeDiv(objName,parentDiv,w,h,content,x,y,overfl,positionType)
{
// positionType could be 'absolute' or 'relative'
if (parentDiv==null) parentDiv='body';
var oDiv = document.createElement ("DIV");
oDiv.id = objName;
if (w) oDiv.style.width = w;
if (h) oDiv.style.height= h;
if (content) oDiv.innerHTML=content;
if (positionType==null) positionType="absolute";
oDiv.style.position = positionType;
if (x) oDiv.style.left=x; else oDiv.style.left=-2000;
if (y) oDiv.style.top=y; else oDiv.style.top=-2000;
if (overfl) oDiv.style.overflow=overfl; else oDiv.style.overflow="hidden";
eval(' document.'+parentDiv+'.appendChild (oDiv); ');
delete oDiv;
}
var snowC=0;
var x = new Array();
var y = new Array();
var speed = new Array();
var t=0;
var cC = new Array();
var ra = new Array();
function makeSnow() {
x[snowC] = Math.round(Math.random()*(browserBredde()-60));
y[snowC] = 10;
makeDiv("snow"+snowC,"body",32,32,'<img src="http://i693.photobucket.com/albums/vv296/KIBBLESGRAMMY/CAT/Orange-tabby-cat-icon.gif">');
speed[snowC] = Math.round(Math.random()*8)+1;
cC[snowC]=Math.random()*10;
ra[snowC] = Math.random()*7;
snowC++;
}
function moveSnow() {
var r = Math.round(Math.random()*100);
if (r>70 && snowC<20) makeSnow();
for (t=0;t<snowC;t++) {
y[t]+=speed[t];move("snow"+t,x[t],y[t]);
if (y[t]>browserHoyde()-50) {y[t] = 10;x[t] = Math.round(Math.random()*(browserBredde()-60));}
cC[t]+=0.01;
x[t]+=Math.cos(cC[t]*ra[t]);
}
setTimeout('moveSnow()',20);
}
moveSnow();
</script>
makeSnow just adds the snowflakes. Stopping that, as you say, does not clear anything. moveSnow handles the animation, and calls itself at a timeout. If instead of setting a timeout for the next moveSnow each time, you set it up to run in an interval just once, you would have an easier time stopping it.
window.snowAnimation = window.setInterval(moveSnow, 20);
If you add a css class to your snow flakes, it would be easier to target them for deletion.
oDiv.className = 'snowflake';
Then your clear function could look something like:
function clearSnow() {
window.clearTimeout(window.snowAnimation);
var flakes = document.getElementsByTagName('snowflake');
for(var i = 0, l = flakes.length; i < l; i++) {
document.body.removeChild(flakes[i]);
}
}
Timeout doesnt help, it helps you only to stop creating new snowdivs, however if you see makeDiv is the one which creates new divs on to the body, if you clear / display:none the divs which got created on makeDiv i hope it will clear all the divs on the screen.
You need to remove the divs that were created. It might be easier if you give them all some sort of class, like ".snowflake" as you create them (in makeDiv), then start removing them from the dom.
You will have to clear the elements created after the time you wanna stop snow falling.
Following code snippet will help you to clear the elements
if(count < 500){
setTimeout('moveSnow()',20);
}else{
var i = 0;
var elem = document.getElementById('snow'+i);
do{
elem.parentNode.removeChild(elem);
elem = document.getElementById('snow'+ ++i);
}while(elem != null)
}
count++;
you have to create a global variable count.

Categories

Resources