I was trying to do something new. Creating a complete javascript game without canvas. It was just going to be a simple Platformer game with squares of different sizes and colors for the Player, obstacles and so on. So far what i've found very challenging to do is the movement. I've found a way around most of the other issues but i cannot figure out how to achieve smooth Platformer style walking and jumping. My attempts have led to the player succeeding to move but always stopping to carry out another movement instead of performing both actions at the same time. For example: Right, Stop, Jump, Stop, Right, Stop, Jump. Here is the script i used (without the keys' actions). I am open to suggestions on how to create smooth Platformer style walking and jumping.
document.onkeydown = KeyPressed;
function KeyPressed(k) {
var LeftBtn = 37;
var RightBtn = 39;
var UpBtn = 38;
var DownBtn = 40;
if (k.keyCode == LeftBtn) {
// Left Arrow Actions
}
if (k.keyCode == RightBtn) {
// Right Arrow Actions
}
if (k.keyCode == UpBtn) {
// Up Arrow Actions
}
}
Please look at the following approach.
I use keydown and keyup event handlers to record which keys are pressed and which are released. So we know if several keys are pressed simultaneously. And we use these records to perform corresponding actions within the separate function running in the requestAnimationFrame loop.
Hope it helps.
var codes = {37:'left', 39:'right', 38:'up', 40:'down'},
step = 3, // pixels per keypress
keys = {left:0, right:0, up:0, down:0};
var c = document.getElementById('c');
requestAnimationFrame(move);
document.onkeydown = KeyPressed;
document.onkeyup = KeyReleased;
function KeyReleased(k) {keys[codes[k.keyCode]] = 0}
function KeyPressed(k) {keys[codes[k.keyCode]] = 1}
function move(t) {
c.style.left = c.offsetLeft + step * (keys.right - keys.left) + 'px';
c.style.top = c.offsetTop + step * (keys.down - keys.up) + 'px';
requestAnimationFrame(move);
}
html, body {overflow:hidden}
#c {
position: absolute;
top: 10%; left: 10%;
font:900 400%/1 sans-serif;
}
Click here and use arrow keys to move.
<div id="c">+</div>
Related
I'm running into a bit of an odd issue with Paper.js - I'm using the library to scale the "petals" of a randomly generated flower while audio plays.
The issue crops up if the flower is "growing" and the user navigates to a different tab in the browser. Even though it appears that the onFrame event is not firing when the window is out of view, whichever petal is currently scaling at the time will continue to scale indefinitely.
I even tried using a special js library to determine if the window is in view and still wasn't able to get the petals to stop scaling.
You can view a demo here, as I was not even able to replicate this in a Paper sketch: https://demos2.paperbeatsscissors.com/
Also including my onFrame code here in case the problem is obvious to someone:
view.onFrame = function(event) {
// See if something is playing
if (playing > -1) {
// Get the active flower
var activeFlower = garden[playing],
activeData = activeFlower.data;
// Active layer and petal
var activeLayer = getEl(activeFlower, activeData.lIndex),
activePetal = getEl(activeLayer, activeData.pIndex);
// Variables
var time = activeData.audio.seek(),
scaleAmount = (1 / (activeData.timing / event.delta.toFixed(3))) * 2;
// Petal progression
if (!activeData.completed) {
if (activePetal.scaling.x < 1 && activePetal.scaling.y < 1) {
activePetal.pivot = {x: 0, y: activePetal.height / 2};
activePetal.scaling.x = activePetal.scaling.x + scaleAmount;
activePetal.scaling.y = activePetal.scaling.y + scaleAmount;
} else {
if (activeData.pIndex < (activeLayer.children.length - 1)) {
// If the petal is scaled, jump to a new petal
activeData.pIndex += 1;
} else {
if (activeData.lIndex > 0) {
// When all petals are bloomed, jump to a new layer
activeData.pIndex = 0;
activeData.lIndex -= 1;
} else {
// Set the flower as completed
activeData.completed = true;
}
}
}
}
activeFlower.rotate(.125, activeData.center);
// Reset the playing variable if the audio clip is complete and the flower has completed
if (!activeData.audio.playing() && time === 0 && activeData.completed) {
playing = -1;
}
}
}
Really stumped on this one so any help is greatly appreciated!
I think that your problem is coming from the fact that you base your scaling calculation on event.delta which represents the time elapsed since the last event fired.
The thing is that, if I'm not mistaken, under the hood, Paper.js onFrame event relies on requestAnimationFrame which does not fire when the tab if inactive.
So when you switch tab, wait for a while and get back to your tab event.delta value is big and your scaling value too, hence the size of your petals. This basic sketch showcase this behavior.
So in my opinion, you should simply check event.delta value and limit it if it's too high.
I am trying to control a GSAP animation using a forward & backward button. The expected behaviour I am aiming for is:
When the user hovers over the forward button the animation moves forward (increments in px).
When the user mouseleaves the button then the animation pauses.
When the user hovers over the backwards button the animation moves the other way.
The issue I have encountered is that I have tried to add a dynamic positioning variable in place which increments when the user hovers over the 'forward' button. This is not working as expected - instead it just moves once instead of waiting until the user's mouse leaves to stop.
I tried to add a setInterval to the button event listener to increment the positioning so that when the user hovered over the button it would move px at a time, which did work, but it would not stop causing the browser to crash. I also added a mouseleave to clear the setInterval but I don't think it was good practise.
var masterTimeline = new TimelineMax();
var mousedown = false;
var forwardBTN = document.getElementById("forward");
var backwardBTN = document.getElementById("backward");
var pauseBTN = document.getElementById("pause");
var blueboxElement = document.getElementById("blueBox");
var direction = '+';
var counter = 0;
var distance = 0;
var value1 = direction + '=' + distance;
var tween;
forwardBTN.addEventListener("mouseenter", function(){
// setInterval(function() {
directionMove('moveForward');
// }, 500);
});
backwardBTN.addEventListener("mouseenter", function(){
directionMove('moveBackward')
});
pauseBTN.addEventListener("click", function(){
directionMove('pause');
});
function directionMove(playk) {
if (playk == 'moveBackward') {
var direction = '-';
value1 = direction + '=' + distance; // how to update value
masterTimeline.to(blueboxElement, 0.1, {css: {x: value1}, ease: Linear.easeNone}); // no need move by default
}
else if (playk == 'moveForward') {
var direction = '+';
value1 = direction + '=' + distance; //how to update value
masterTimeline.to(blueboxElement, 0.1, {css: {x: value1}, ease: Linear.easeNone}); // no need move by default
}
else if (playk == 'pause') {
masterTimeline.kill();
console.log("killed");
//
}
}```
The expected behaviour is to move forward incrementally without stopping until the user moves off the forward button but at present it is just moving a single amount one time.
Here is a CodePen link if this helps:
https://codepen.io/nolimit966/pen/pXBeZv?editors=1111
I think you're overcomplicating this a bit (at least in the demo). The entire point of GSAP is moving things along a timeline. What you're trying to do is essentially forcibly use a timeline to work in a non-timeline way, which is why I think you're having trouble.
If you step back and just think about your three requirements, I think it becomes a lot more simple. It's always good to think about it in words like you did because it can help you simplify it and understand it better.
The key steps are to:
Create a timeline for the movement of the box.
Play the timeline forward when the forward button is hovered.
2b. Pause it when it's no longer hovered.
Play the timeline backward when the reverse button is hovered.
3b. Pause it when it's no longer hovered.
In code that looks like this:
var tl = new TimelineMax({paused: true});
var forwardBTN = document.getElementById("forward");
var backwardBTN = document.getElementById("backward");
var pauseBTN = document.getElementById("pause");
var blueboxElement = document.getElementById("blueBox");
tl.to(blueboxElement, 10, {x: window.innerWidth - blueboxElement.clientWidth, ease: Linear.easeNone});
function pause() {
tl.pause();
}
forwardBTN.addEventListener("mouseenter", function() {
tl.play();
});
forwardBTN.addEventListener("mouseleave", pause);
backwardBTN.addEventListener("mouseenter", function() {
tl.reverse();
});
backwardBTN.addEventListener("mouseleave", pause);
Demo
By the way, you're much more likely to get a faster response over on the official GreenSock forums :)
Do you mean something like this?
var tl = new TimelineMax({paused: true});
tl.to('.element', 3, {
x: 800,
});
$(document).on({
mouseenter: function () {
if($(this).hasClass('forward')){
tl.play();
}
else if($(this).hasClass('backwards')){
tl.reverse();
}
},
mouseleave: function () {
tl.pause();
}
}, ".btn");
.wrapper{
width: 100%;
padding: 40px;
}
.element{
width: 100px;
height: 100px;
background: red;
}
.btn{
display: inline-block;
padding: 15px;
background: #bbb;
margin-top: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
<div class="element_wrapper">
<div class="element"></div>
</div>
<div class="forward btn">Forward</div>
<div class="backwards btn">Backwards</div>
</div>
Here I have an odd problem. I'm building a simple helicopter game (the old type - click to go up, avoid obstacles). My problem is that the obstacles generate, but don't position correctly, and then they won't move. I'm trying to move them with jQuery's css() - the css method works fine on anything else but when used with top and left doesn't.
The problem functions (generate and move obstacles):
game.background.generateObs = function() {
var top = Math.floor(Math.random()*game.canvas.height);
var left = game.canvas.width-10;
var $obs = $("<div></div>")
$obs.addClass("obs").appendTo("#canvas");
$obs.css({
background: "black",
position: "absolute",
height: game.obstacle.height,
width: game.obstacle.width,
});
$obs.css("top", $("#canvas").offset().top + top )
.css("left",$("#canvas").offset().left + left);
game.obstacle.width = Math.floor(Math.random()*200);
if(game.gameState=="running") {
setTimeout("game.background.generateObs()",obsInterval);
}
else {
return;
}
}
game.background.moveObs = function() {
var currentPos = $("#canvas div.obs").css("left");
var newPos = currentPos - game.obstacle.frameWidth;
$("#canvas div.obs").css("left",newPos);
if(game.gameState=="running") {
setTimeout("game.background.moveObs()",interval);
}
else {
return;
}
}
The other thing is that jsFiddle is now telling me that game is undefined, when I have defined it right at the start.
Can anyone tell me what I'm doing wrong? Here's the fiddle.
$("#canvas div.obs").css("left") is returning auto in your fiddle, not a number.
Try using .offset().left instead.
Additionally, you should change your setTimeout calls like this:
setTimeout(game.background.moveObs,interval);
I'm developing a Fez-based HTML5 Canvas game using EaselJS and I've found a problem, that I can't solve myself, while trying to implement SpriteSheets to the hero.
The problem is that I've defined three animations to my hero ("stand", "jump" and "run") and when I try to change from one animation to another using hero.gotoAndPlay("run") or hero.gotoAndStop("stand") the animations don't change, change but show the first frame only or change to the wrong animation.
Can someone help me? What I'm doing wrong and how can I fix it? Thanks.
Here's the relevant JavaScript code I'm using to create the hero Sprite:
var data = {
images: ["http://i.imgur.com/M0GmFnz.png"],
frames: {width:34, height:46},
animations: {
stand:0,
run:[0,12,true,0.25],
jump:13
}
};
var spriteSheet = new createjs.SpriteSheet(data);
var hero = new createjs.Sprite(spriteSheet, "stand");
hero.offset = 4 + 1; // "+1" is important, change the first number only.
hero.regX = hero.offset + 2;
hero.regY = hero.offset;
hero.width = 34 - (hero.offset*2) - 12;
hero.height = 46 - (hero.offset*2);
hero.x = 0;
hero.y = 0;
hero.jumping = true;
stage.addChild(hero);
And the code I'm using to change the animation:
if(!left && !right){ // User isn't pressing left arrow or right arrow
hero.gotoAndStop("stand");
}else{
hero.gotoAndPlay("run");
}
JSFiddle
Official Site
If you are calling gotoAndStop or gotoAndPlay in a tick (or similar) then it will constantly reset to the first frame. You have to ensure you only call these functions one time when the animation changes.
A good way to set this up is to store the current animation, store it, and only change it up if the animation changes. Something like:
var animation;
if(!left && !right){ // User isn't pressing left arrow or right arrow
animation = "stand";
}else{
animation = "run";
}
if (currentAnimation != animation) {
hero.gotoAndStop(animation);
}
This is just an example, but should give you an idea. You should only call these methods one time to kick off an animation.
this is my code :
var pane = $('#Container'),
box = $('#PLayer'),
w = pane.width() - box.width(),
d = {},
x = 3;
function newv(v, a, b) {
var n = parseInt(v, 10) - (d[a] ? x : 0) + (d[b] ? x : 0);
return n < 0 ? 0 : n > w ? w : n;
}
$(window).keydown(function (e) { d[e.which] = true; });
$(window).keyup(function (e) { d[e.which] = false; });
setInterval(function () {
box.css({
left: function (i, v) { return newv(v, 37, 39); },
top: function (i, v) { return newv(v, 38, 40); }
});
}, 20);
<div id="Container" class="Container">
<div id="PLayer" class="player" ></div>
</div>
by this code i managed to make the div animate by using the arrow key but how can i make that jumping effect ? like the one used here
Here's a link to a fork of your fiddle:
http://jsfiddle.net/dJut2/
I made two changes:
Jquery wanted top to have an initial value while using the 2-argument callback inside .css(...), so I set it to 50% in CSS: .player{ ... top: 50%; }
I added "gravity" using another ternary check inside the input response/collision function. It checks against whether 38/the up-key is passed as an input case and adds 2 to the top: ... + (a==38 ? 2 : 0)
By the way, verbose variable names or comments could really make your code easier to read. Also, ternary operators aren't as efficient or readable as some if statements. Lastly, you should break your input response and collision functionality into specific cases so you can edit them each specifically later without having to refactor.
Lastly, if you want to simulate a 'jump', you'll need to have some kind of timed input case. This can be accomplished automatically with an acceleration variable (set it to a number, subtract it from the top every frame, and decrement it every frame flooring at 0 when on the ground; the gravity I added can be removed in this case) or some kind of specific animation (subtract 4 every frame for 20 frames).
If you want to do something similar to that platformer game you linked (which is awesome), why not just check out the source code for it? http://taffatech.com/Platformer.js
if(Player.isUpKey == true)
{
if(!Player.jumping){
Player.jumping = true;
Player.velY = -Player.speed*2;
}
}
If you look there you see he's not actually using CSS animations, but recalculating the player's position every frame. If the player hit's the up key, their y velocity is increased, and then every frame this is called to move the player
//gravity would be a numeric variable, this makes the player constantly move
//back downwards when they leave the ground
Player.velY += gravity;
//adjusting the player's position according to new
//calculated x and y velocity
Player.x += Player.velX;
Player.y += Player.velY;
This is similar to how most game engines work, recalculating the scenario and forces each frame so they can constantly react to what's going on. Instead of animations, a physics engine is doing the math and moving things accordingly on screen.