I'm trying to create a simple animation effect of a pendulum swinging back and forth. I'm only using a single image sprite for it, and I've got the code below, but the animation only tilts to a 45 angle then stops completely. What might I be doing wrong?
var title = new Phaser.Scene("GameTitle");
var pendulum;
var direction;
title.create = function(){
pendulum = this.add.sprite(200, 0, 'titlePendulum').setOrigin(0.5, 0).setScale(1.8).setRotation(79);
direction = "left";
};
title.update = function(){
console.log(pendulum.angle.toFixed(0));
swing(direction);
if(pendulum.angle.toFixed(0) == 71){
swing ("right");
}
if(pendulum.angle.toFixed(0) == -76){
swing("left");
}
};
function swing(dir){
if(dir == "left"){
if(pendulum.angle.toFixed(0) == 71){
swing("right");
}else{
pendulum.angle +=1.5;
}
}else{
if(pendulum.angle.toFixed(0) > -80){
pendulum.angle -= 1.5;
}
if(pendulum.angle.toFixed(0) == -76){
swing("left");
}
}
}
I noticed that you check if the pendulum.angle is larger than or exactly equal to 80 in two separate if statements. However 80 is not divisible by 1.5, and the value of the angle could be .. 76, 77.5, 79, 80.5 .. and the condition pendulum.angle.toFixed(0) == 80 will never be true.
Also, the name of the function parameter is dir but you check for a direction.
I'm pretty sure that is causing the problem. Even if that's not it, it's still better (more elegant and "cleaner") to use if-else when you are checking for a condition that can either be one thing or the other, instead of using two separate if statements.
So change your code to something like this:
function swing(dir) {
if (dir == "left") { // check parameter, not the direction global var
if (pendulum.angle.toFixed(0) < 80) {
pendulum.angle +=1.5;
} else {
swing("right");
}
} else { // dir == "right"
if (pendulum.angle.toFixed(0) > -80) {
pendulum.angle -= 1.5;
} else {
swing("left");
}
}
}
Related
I'm a js newbie but from what I read, using clearInterval with id returned previously by setInterval should reset the timer.
On a pedal push I receive a midi event with a positive value and then same event with a zero value on pedal release.
Using the code below, I can see my page turning red on release but the page keeps scrolling. Any idea why ?
function handleMIDIMessage(event) {
var scroll_id;
if (event.data.length === 3) {
if (event.data[0] == 176 && event.data[1] === 67) {
if (event.data[2] > 0) {
scroll_id = setInterval(function() {
window.scrollBy({
top: 50,
behaviour: "smooth"
});
}, 1000);
document.body.style.background = 'green';
} else {
document.body.style.background = 'red';
clearInterval(scroll_id);
}
}
}
}
You need to declare the interval outside so you do not keep creating a new variable and losing the id. You should probably also check to make sure you are not creating more than one interval.
//declare it so it is not overwritten
var scroll_id;
function handleMIDIMessage(event) {
if (event.data.length === 3) {
if (event.data[0] == 176 && event.data[1] === 67) {
if (event.data[2] > 0) {
// if we were defined before, cancel the last one
if (scroll_id) window.clearInterval(scroll_id);
scroll_id = setInterval(function() {
window.scrollBy({
top: 50,
behaviour: "smooth"
});
}, 1000);
document.body.style.background = 'green';
} else {
document.body.style.background = 'red';
clearInterval(scroll_id);
}
}
}
}
I am trying to move a sprite using arrow keys. Below is the logic that SHOULD cause the sprite to move diagonally if two keys are pressed. However, that is not the case. It will only move in one direction a a time. Since I am saving each keyCode as a Boolean within an object property whenever I press a button, I would think that this should not be an issue, but I can't get it to work. I have also tried using an array to store these Booleans, and checking those values instead. No luck.
I am at my wit's end and would greatly appreciate some help. I've scoured stackoverflow and many different blogs on building 2d games, but but nothing that I try works.
var keyState = {};
window.onkeydown = window.onkeyup = function(e) {
keyState[e.keyCode] = e.type == 'keydown';
// checks for up and left
if (keyState[38] && keyState[37]) {
player.pos.x -= 2;
player.pos.y -= 2;
}
// checks for up and right
else if (keyState[38] && keyState[39]) {
player.pos.x += 2;
player.pos.y += 2;
}
// checks for down and left
else if (keyState[40] && keyState [37]) {
player.pos.x -= 2;
player.pos.y -= 2;
}
// checks for down and right
else if(keyState[40] && keyState [39]) {
player.pos.x += 2;
player.pos.y -= 2;
}
// checks for up
else if (keyState[38]) {
player.animation.y = 64;
player.pos.y -= 2;
}
};
You should explicitly set the object key values as booleans and you should toggle them in separate keydown/keyup handlers:
let keysdown = {};
window.addEventListener('keydown', function(evt) {
keysdown[evt.which] = true;
if (keysdown["38"] === true && keysdown["37"] === true) {
console.log('up & left');
} else if (keysdown["38"] === true && keysdown["39"] === true) {
console.log('up & right');
} else if (keysdown["40"] === true && keysdown["37"] === true) {
console.log('down and left');
} else if (keysdown["40"] === true && keysdown["39"] === true) {
console.log('down and right');
} else if (keysdown["38"] === true) {
console.log('up');
}
}, false);
window.addEventListener('keyup', function(evt) {
keysdown[evt.which] = false;
}, false);
This logs all the correct key combinations for me.
You need to move the movement logic out of the event listener. Have the key event just log the current state of each key (up or down) then in the main loop check the state and move the play as needed.
As I am unsure if you want the movement to be constant or only on the press I have included the option to change the behaviour with the constant flag MOVE_ONLY_ON_PRESS if true movement only when a press is first detected. So that no key strokes are missed I clear the key flag in the main loop.
The following is an example of how to interface with the keyboard for a game.
// global key log;
var keyState = [];
const KEY_UP = 38;
const KEY_DOWN = 40;
const KEY_LEFT = 37;
const KEY_RIGHT = 39;
// Player
var player = {};
player.x = 100;
player.y = 100;
const MOVE_SPEED = 2;
const MOVE_ONLY_ON_PRESS = false; // This will toggle constant movement or only on press
// from your code
window.onkeydown = window.onkeyup = function (e) {
if (MOVE_ONLY_ON_PRESS) {
if (e.type === 'keydown') {
keyState[e.keyCode] = true;
}
} else {
keyState[e.keyCode] = e.type == 'keydown';
}
}
// in the main loop;
function update(timer) {
// you dont need to test for the combinations (ie up left) when its just simple movement
if (keyState[KEY_UP]) {
player.y -= MOVE_SPEED;
if (MOVE_ONLY_ON_PRESS) { keyState[KEY_UP] = false; }
}
if (keyState[KEY_DOWN]) {
player.y += MOVE_SPEED;
if (MOVE_ONLY_ON_PRESS) { keyState[KEY_DOWN] = false; }
}
if (keyState[KEY_LEFT]) {
player.x -= MOVE_SPEED;
if (MOVE_ONLY_ON_PRESS) { keyState[KEY_LEFT] = false; }
}
if (keyState[KEY_RIGHT]) {
player.x += MOVE_SPEED;
if (MOVE_ONLY_ON_PRESS) { keyState[KEY_RIGHT] = false; }
}
requestAnimationFrame(update);
}
requestAnimationFrame(update);
Bit fields.
Another way to simplify the arrow keys is to set the first 4 bits of a number to correspond to a key, one bit per key, then it is easy to test for combinations of keys as each of the 16 possible combinations has a unique number. You Could map many more keys this way but it becomes a little impractical when you go to far.
This is how to set them
var arrowBits = 0; // the value to hold the bits
const KEY_BITS = [4,1,8,2]; // left up right down
const KEY_MASKS = [0b1011,0b1110,0b0111,0b1101]; // left up right down
window.onkeydown = window.onkeyup = function (e) {
if(e.keyCode >= 37 && e.keyCode <41){
if(e.type === "keydown"){
arrowKeys |= KEY_BITS[e.keyCode - 37];
}else{
arrowKeys &= KEY_MASKS[e.keyCode - 37];
}
}
}
These are 8 of the 16 possible combinations
// postfix U,D,L,R for Up down left right
const KEY_U = 1;
const KEY_D = 2;
const KEY_L = 4;
const KEY_R = 8;
const KEY_UL = KEY_U + KEY_L; // up left
const KEY_UR = KEY_U + KEY_R; // up Right
const KEY_DL = KEY_D + KEY_L; //
const KEY_DR = KEY_D + KEY_R; //
This is how you test for then
if ((arrowBits & KEY_UL) === KEY_UL) { // is UP and LEFT only down
if (arrowBits & KEY_UL) { // is Up left down (down and right may also be down)
if ((arrowBits & KEY_U) === KEY_U) { // is Up only down
if (arrowBits & KEY_U) { // is Up down (any other key may be down)
if (!(arrowBits & KEY_U)) { // is Up up (any other key may be down)
if (!arrowBits) { // no keys are down
if (arrowBits) { // any one or more keys are down
I cannot tell you why your code doesn't work. If I replace the player-movements with console-logs, it works for me.
Although it's pretty primitive.
First separate the keystate-manager from the movement-logic.
var isKeyDown = (function(alias){
for(var i=0, a=Object.create(null); i<256;) a[i++]=false;
for(var k in alias) i=0|alias[k], i>0 && i<256 && Object.defineProperty(a,k,{get:Function("return this["+i+"]")});
function keyStateHandler(e){
a[e.which||e.keyCode] = e.type==="keydown"
}
addEventListener("keydown", keyStateHandler, false);
addEventListener("keyup", keyStateHandler, false);
return a;
})({
//add some aliases for more readable code
up: 38,
left: 37,
right: 39,
down: 40
});
once, because the update-interval of the keydown-event is not reliable, varies between browsers, and is not sync with the update-interval of the brwoser wich will cause flickering.
Now create a main loop for your game, and take account for the timing. Although requestAnimationFrame aims for a constant framerate you may have lags, and you don't want to see your player move at different speeds during these lags.
var lastCall = Date.now();
function tick(){
requestAnimationFrame(tick);
var now = Date.now(),
time = (now - lastCall) / 1000; //time in seconds
//because it's easier to think in terms like 2px/s
//than 0.002px/ms or 0.0333 px/frame
lastCall = now;
move(time);
}
tick();
now the actual movement:
//the speed of your player: it would be better if this would be
//a property of the player than hardcoded like you did, or global like this
var speed = 2; // 2px/second
function move( time ){
//accounts for varying framerates
//opposite directions cancel each other out
var dx = (isKeyDown.right - isKeyDown.left) * time,
dy = (isKeyDown.down - isKeyDown.up) * time;
//taking account for diagonals
if(dx && dy) dx /= Math.SQRT2, dy /= Math.SQRT2;
player.pos.x += dx * speed;
player.pos.y += dy * speed;
}
Based on skyline3000 answer I tried to make it work also in down, left and right. Appart from adding the other options I had to change window to document to make it work:
let keysdown = {};
document.addEventListener('keydown', function (evt) {
keysdown[evt.which] = true;
if (keysdown["38"] === true && keysdown["37"] === true) {
prota.moveUp();
prota.moveLeft();
// console.log('up & left');
} else if (keysdown["38"] === true && keysdown["39"] === true) {
prota.moveUp();
prota.moveRight();
// console.log('up & right');
} else if (keysdown["40"] === true && keysdown["37"] === true) {
prota.moveDown();
prota.moveLeft();
// console.log('down and left');
} else if (keysdown["40"] === true && keysdown["39"] === true) {
prota.moveDown();
prota.moveRight();
// console.log('down and right');
} else if (keysdown["38"] === true) {
prota.moveUp();
// console.log('up');
} else if (keysdown["40"] === true) {
prota.moveDown();
// console.log('down');
} else if (keysdown["39"] === true) {
prota.moveRight();
// console.log('right');
} else if (keysdown["37"] === true) {
prota.moveLeft();
// console.log('left');
}
}, false);
document.addEventListener('keyup', function (evt) {
keysdown[evt.which] = false;
}, false);
I'm trying to add a health meter representation to a game I'm working on and stuck on a DOM manipulation problem. The idea is that there is an image that deteriorates in various stages as the player loses health. Here is an example of what I have so far.
var healthDisplay = document.getElementById('healthDisplay');
if (heroHealth > 75) {
healthDisplay.src = 'image1.png';
} else if (heroHealth < 75 || heroHealth > 30){
healthDisplay.src = 'image2.png';
} else {
healthDisplay.src = 'image3.png';
};
That's just the basics of it. My question is about the next step. What do I add to make it so the script is constantly checking to see if these parameters are true or false? Would it be best to put it in a while loop, or add an event listener?
You'd want to put it in a setinterval.
//this is assuming that heroHealth, and healthDisplay
//are available to this function already
//either through closure or they are global
//variables etc.
function checkHealth () {
if (heroHealth > 75) {
healthDisplay.src = 'image1.png';
} else if (heroHealth < 75 && heroHealth > 30){
healthDisplay.src = 'image2.png';
} else {
healthDisplay.src = 'image3.png';
};
}
setInterval(checkHealth, 100);
The setInterval calls a function repeatedly at certain intervals. This example is set to 100 milliseconds. This way as the hero's health goes up and down, the image src will change as the function fires.
Depending on how your game is set up and how the health is stored, you could wire up the function to an on change event:
<input type="textbox" id="heroHealthCheckBox"
onchange="checkHeroHealth(this.value)"/>
function checkHeroHealth(health) {
var heroHealth = parseInt(health);
if (heroHealth > 75) {
healthDisplay.src = 'image1.png';
} else if (heroHealth < 75 && heroHealth > 30){
healthDisplay.src = 'image2.png';
} else {
healthDisplay.src = 'image3.png';
}
}
I also changed the second conditional from
heroHealth < 75 || heroHealth > 30
to this
heroHealth < 75 && heroHealth > 30
When I click any of the 2 radio buttons the text overlaps. I want the if else conditions to run seperate. That means when I click the higher radio button, it checks if playertotal is higher than computertotal and writes the outcome. The same with the lower radio button.
This belongs to a longer code:
function button1()
{
// Checks if it's the players turn
if(playerturn==true)
{
// Generates eyes for the dice
var eyes3=Math.floor(Math.random()*6)+ 1;
var eyes4=Math.floor(Math.random()*6)+ 1;
//Shows the eyes
showEyes(eyes3, 325);
showEyes(eyes4, 450);
computerturn=true;
playerturn=false;
playerTotal = eyes3+eyes4;
//Checks if player has won
if (higher=1 && playerTotal > computerTotal)
{
pen.font = '20pt Calibri';
pen.fillText('You have won',300,250);
}
else if (higher=1 && playerTotal < computerTotal)
{
pen.font = '20pt Calibri';
pen.fillText('You have lost',300,250);
}
if (lower=1 && playerTotal < computerTotal)
{
pen.font = '20pt Calibri';
pen.fillText('You have won!',300,250);
}
else if (lower=1 && playerTotal > computerTotal)
{
pen.font = '20pt Calibri';
pen.fillText('You have lost',300,250);
}
}
}
function higherRadioButton()
{
higher = 1
lower = 0
}
function lowerRadioButton()
{
lower = 1
higher = 0
}
Thank you.
In JavaScript, the comparison operator is ==, not =. Your code should look like this:
if (higher == 1 && playerTotal > computerTotal)
{
//...
}
And same for all other statements.
To avoid future problems of this sort, always put the number to the left:
if (1 == higher && playerTotal > computerTotal)
{
//...
}
This way if you forget one "=" you will get error instead of wrong results.
I want to scroll to a div each time user presses j key. Here is the code for it.
$(function() {
function scroll(direction) {
var scroll, i,
positions = [],
here = $(window).scrollTop(),
collection = $('.message_box');
collection.each(function() {
positions.push(parseInt($(this).offset()['top'],0));
});
for(i = 0; i < positions.length; i++) {
if (direction == 'next' && positions[i] > here) { scroll = collection.get(i); break; }
if (direction == 'prev' && i > 0 && positions[i] >= here) { scroll = collection.get(i); break; }
}
if (scroll) {
$('html, body').animate({"scrollTop": $(scroll).offset().top-50});
$(scroll).css('color', 'blue');
$(scroll).mouseleave(function() {
$(this).css('color', 'black');
});
}
return false;
}
$("#next,#prev").click(function() {
return scroll($(this).attr('id'));
});
$('body').keyup(function(event) {
if (event.which == 74) {
return scroll('next');
}
});
$('body').keyup(function(event) {
if (event.which == 75) {
return scroll('prev');
}
});
});
I need to subtract 50 from the offest of the div to scroll to which is this.
$('html, body').animate({"scrollTop": $(scroll).offset().top-50});
It will scroll the first time but not the rest of the times. I always get the integer 218 which is the offset of the first div to scroll to.
DEMO - http://jsfiddle.net/XP5sP/6/
Can someone help me ?
The problem is that you're always moving the scrollTop value to 50 pixels before the first matched element, so it's always identifying that element as the one you need to scroll to in your if statement because its position is greater than the current scrollTop value.
Modify the relevant section of your code to this:
if (direction == 'next' && positions[i] > here + 50) {
scroll = collection.get(i);
break;
}
That way it accounts for the window being scrolled to 50 pixels above the current element.
$(scroll).offset().top-50 is prefectly valid, as .top will return an integer value. Therefore the issue is not with this portion of your code.
I suspect the issue is to do with the scroll variable you have within the scroll function. I always keep away from naming my variables the same as my function names when within the same scope.
Try putting some space between your minus symbol so it does not get mistaken for a dash.
$('html, body').animate({"scrollTop": $(scroll).offset().top - 50});
or save your mathematics in a variable first
var scrollMinusFifty = $(scroll).offset().top - 50;
$('html, body').animate({"scrollTop":scrollMinusFifty});