Intruduction:
I am writing a simple animation with JavaScript and PIXI.js.
How it's working:
I paint textures in new places and delete it in old places by every step.
Problem:
Sometimes i get these results(some textures are not displayed and CPU loaded on 50%)
http://itmages.ru/image/view/2649716/a5ae37b5
But if i updating the page i can get normal results (not always) and CPU loaded on 2-3%
http://itmages.ru/image/view/2649736/ca696082
Code
!)function animate does one step of animation
There are 3 versions:
1)
anim();
function anim() {
setTimeout(function() {
requestAnimationFrame(anim);
animate();
}, 40);
}
2)setInterval(function() {requestAnimationFrame(animate);}, 50);
3)setInterval(animate, 50);
I loading pictures with that function:
function presets()
{
unit_texture = new PIXI.Texture.fromImage('/assets/unit_2.png');//('/images/unit_2.png')
shell_texture = new PIXI.Texture.fromImage('/assets/shell.png'); //('/images/shell.png')
}
unit_2.png is about 377 Bytes and it's resolution is (19 x 20)
shell.png is about 30 KB and it's resolution is (200x200)
After loading i use these textures to make sprites (PIXI)
function Unit(id, x, y, energy, hp)
{
this.id = id;
this.x = x;
this.y = y;
this.energy = energy;
this.hp = hp;
this.sprite = new PIXI.Sprite(unit_texture);
this.sprite.width = 2 * 50;
this.sprite.height = 2 * 50;
this.sprite.anchor.x = 0.5;
this.sprite.anchor.y = 0.5;
this.sprite.position.x = x;
this.sprite.position.y = y;
stage.addChild(this.sprite);
}
At every step i delete all old Unit objects and create new Unit objects.
(I can't just move them because of organizaion of my system).
I think the biggest trap here is making sprite many times, but i could not fix it yet.
PIXI.Texture.fromImage function is asynchronous.
http://www.html5gamedevs.com/topic/2620-settexture-doesnt-always-use-preloaded-images/
Possible solution of this problem is here:
http://www.html5gamedevs.com/topic/7674-load-textures-synchronously/
Related
I'm in the middle of creating this simple animation using HTML5 Canvas and JavaScript and I'm experiencing a problem with flickering objects.
I was trying to find the solution on the internet before I asked this question and all I found was basically:
avoid loading new image , object at each new frame
use requestAnimationFrame()
I think I've done that all and the flickering is still happening.
(blue rectangles (obstacles) in my case.
The only solution that works is reducing the number of pixels in method responsible for moving the object, here:
obstacle.prototype.moveObstacle = function(){
this.x -=3
}
but the the animation is too slow.
Is there any way around it?
JSFiddle: https://jsfiddle.net/wojmjaq6/
Code:
var cnv = document.getElementById("gameField");
var ctx = cnv.getContext("2d");
var speedY = 1
var obst1 = new obstacle(cnv.width + 50);
var myBird = new bird(100, 1);
function bird(x, y) {
this.x = x;
this.y = y;
this.gravity = 0.3
this.gravitySpeed = 0
}
bird.prototype.drawbird = function() {
ctx.fillStyle = "red"
ctx.fillRect(this.x, this.y, 20, 20);
}
bird.prototype.animate = function() {
this.gravitySpeed += this.gravity
this.y += speedY + this.gravitySpeed
}
function obstacle(x) {
this.x = x;
this.y = 0;
this.obstLen = Math.floor(Math.random() * 400)
}
obstacle.prototype.drawobstacle = function() {
ctx.fillStyle = "blue";
ctx.fillRect(this.x, this.y, 15, this.obstLen)
ctx.fillRect(this.x, cnv.height, 15, -(cnv.height - this.obstLen - 100))
}
obstacle.prototype.moveObstacle = function() {
this.x -= 3
}
function myFun() {
ctx.clearRect(0, 0, cnv.width, cnv.height);
myBird.animate();
myBird.drawbird();
obst1.moveObstacle();
obst1.drawobstacle();
if (obst1.x < 0) {
obst1 = new obstacle(cnv.width + 50);
}
window.requestAnimationFrame(myFun)
};
function test() {
if (myBird.gravity > 0) {
myBird.gravity = -1
} else {
myBird.gravity = 0.3
}
}
document.getElementById("gameField").onmousedown = test
document.getElementById("gameField").onmouseup = test
window.requestAnimationFrame(myFun)
I do see some stuttering with the blue obstacle - the animation is not smooth.
Changing the x position of the obstacle based on the raw requestAnimationFrame loop will not necessarily result in a smooth operation as requestAnimationFrame just requests that the browser re-draws when it can.
The time between calls to requestAnimationFrame can vary depending on the power of the device the animation is on and how much there is to do each frame. There is no guarantee that requestAnimationFrame will give you 60 FPS.
The solutions are to decouple the changing of objects positions with the actual drawing of them, or factor it the elapsed time between frames and calculate the new position based on that to give a smooth animation.
Normally in my canvas animations I just use a library like GreenSock's Animation Platform (GSAP) https://greensock.com/get-started-js which can animate any numeric property over time, then I only have to write code for the drawing part.
It is possible to compute a time based animation in your own requestAnimationFrame, though there is a bit of complexity involved. This looks like a good tutorial on it http://www.javascriptkit.com/javatutors/requestanimationframe.shtml
Cheers,
DouG
I am writing a game engine for Javascript and am attempting to allow my sprites to be rotated.
However, when I rotate my sprites, the image gets skewed. It rotates, but it is not the correct dimensions.
I am following the same basic logic that I use when programming in Java with the Image object and 2d libraries but getting different results. (Image skewed and shouldn't be... just needs to be rotated)
function Sprite(imgg,w,h)
{
this.img = imgg;
this.x = 350;//Math.random()*700;
this.y = 350;//Math.random()*700;
this.vx = 0;//Math.random()*8-4;
this.vy = 0;//Math.random()*8-4;
this.width = w;
this.height = h;
this.rotatespeed = 0;//0.01;
this.rotate = 40;
}
function drawSprite(sprite, ctx)
{
ctx.save();
ctx.translate(sprite.x,sprite.y);
ctx.rotate(sprite.rotate);
ctx.drawImage(sprite.img,0,0,sprite.img.width,sprite.img.height,-sprite.width/2,-sprite.height,sprite.width,sprite.height);
ctx.restore();
}
function drawGame(g)
{
var gameLoop = setInterval(function(){
g.context.clearRect(0,0,g.canvas.width, g.canvas.height);
g.context.save();
g.context.translate(g.canvas.width/2, g.canvas.height/2);
g.context.scale(g.camera.scale,g.camera.scale);
g.context.rotate(g.camera.rotate);
g.context.translate(g.camera.x,g.camera.y);
for(var i=0;i<g.objects.length;i++)
{
updateSprite(g.objects[i]);
drawSprite(g.objects[i], g.context);
}
g.context.restore();
},setIntervalAmount);
}
Turns out that everything was okay, but the web browser was very stretched in one dimension because I had the developer console up, so it was all an optical illusion.
I've made a script where there are supposed to be little balls that attract eachother in real time. The problem it is EXTREMELY slow. I used animation frame, so I think it should be updating every frame, but it isn't. Here is the code:
$(function() {
var mouseDown
var c = document.getElementById('myCanvas');
var ctx = c.getContext("2d");
var objects = []
c.addEventListener("mousedown", onMouseDown);
c.addEventListener("mouseup", onMouseUp);
function createSquare(x, y, size, direction, xVel, yVel) {
this.x = x;
this.y = y;
this.size = size;
this.drawStylus = drawStylus;
};
function drawStylus() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
ctx.fill();
};
function getDistance(x1, y1, x2, y2) {
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
}
function draw() {
ctx.clearRect(0, 0, 5000, 5000);
for (i = 0; i < objects.length; i++) {
var x = objects[i][0]
var y = objects[i][1]
var size = objects[i][2]
var dir = Math.random() * Math.PI * 2
var force = 0
var xVel = 0
var yVel = 0
for (n = 0; n < objects.length; n++) {
if (n != i) {
force = 100 * objects[n][2] / getDistance(x, y, objects[n][0], objects[n][1])
angle = Math.atan2(y - objects[n][1], x - objects[n][0])
xVel += force * -Math.cos(angle)
yVel += force * -Math.sin(angle)
window.requestAnimationFrame(draw)
};
};
ctx.beginPath();
ctx.arc(x + xVel, y + yVel, size, 0, 2 * Math.PI);
ctx.fill();
};
};
function onMouseDown() {
mouseDown = true
x = event.clientX
y = event.clientY
size = 100
animation = function() {
size = size + 20
var cursorSquare = new createSquare(x, y, size);
cursorSquare.drawStylus();
anim = window.requestAnimationFrame(animation)
};
window.requestAnimationFrame(animation)
};
function onMouseUp() {
if (mouseDown) {
window.cancelAnimationFrame(anim)
var newSquare = new createSquare(x, y, size);
objects.push([x, y, size])
mouseDown = false
};
};
function loop() {
draw();
window.requestAnimationFrame(loop);
};
function init() {
loop();
};
init()
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<canvas id='myCanvas' width="5000" height="5000" style="border:1px solid #000000;"></canvas>
You are calling requestAnimationFrame for each object, this is the wrong way to use requestAnimationFrame (RAF).
You should only call it once per frame not once per object.
function mainLoop(time){ // main loop RAF will add the time in milliseconds to the arguments.
ctx.clearRect(0,0,canvas.width,canvas.height); // clear
draw(); // call the draw loop
requestAnimationFrame(loop); // request next frame
}
requestAnimationFrame(loop); // request next frame
Using the draw functions like ctx.arc is very slow. You will get much better performance if you render images instead ctx.drawImage. You can create a canvas, draw the arc on that canvas and then draw that canvas with ctx.drawImage(canvasImage,... to get a much faster update.
The other answer advised you to use forEach, don't use forEach or any of the array functions that involve callbacks as they are MUCH slower than using standard loops (for, while, do)
UPDATE
As things change rapidly in the browser world I have tested the use of forEach in this case and in this case the news is not good. forEach still adds a significant additional overhead on each iteration when compared to for, while , and do while
The important thing to note (and why I striked out the last paragraph) is that the overhead is per iteration, if you have a small number of iterations and a large amount of code per iteration then the overhead is insignificant and not worth the bother, personal coding style should make the choice of what style to use in those cases.
If on the other hand you have a large number of iterations and a small amount of processing per iteration then using forEach will significantly impact the performance of the loop.
This holds true for Chrome, Edge, and Firefox with all showing the standard iteration (for loops) with inline code (not calling a function) to be the quickest, next and 10% slower than standard iteration is standard iteration with a function call (like forEach), and then forEach with an additional overhead per iteration of over 2X. (each test used a 15-20 to 1 code balance, that is the code inside the iteration is 15-20 times longer than the minimum code required to iterate. So one line for the for, forEach loop and 10-15 lines of code inside the loop.)
If you are handling an array of a few thousand to tens of thousands the difference is not worth bothering with, If you are handling 100s of thousands to millions plus you should avoid forEach.
Note: I did not test forEach on typed arrays as that is not applicable in this case.
Tested on
Chrome Version 50.0.2661.37 beta-m
Firefox 46.0b2
Edge 25.10586
A couple of things that might help.
Take objects.length out of the for loop and assign it to a var before you start the loop. Currently your counting the length of objects on every interaction of your loop.
Better yet use objects.forEach to iterate over the arrays.
Lastly why does draw() call itself at the bottom of the two for loops? This is going to fill up the event loop very quickly and suspect the main reason for the slow down.
I'd like to throw a ball (with an image) into a 2d scene and check it for a collision when it reached some distance. But I can't make it "fly" correctly. It seems like this has been asked like a million times, but with the more I find, the more confused I get..
Now I followed this answer but it seems, like the ball behaves very different than I expect. In fact, its moving to the top left of my canvas and becoming too little way too fast - ofcouse I could adjust this by setting vz to 0.01 or similar, but then I dont't see a ball at all...
This is my object (simplyfied) / Link to full source who is interested. Important parts are update() and render()
var ball = function(x,y) {
this.x = x;
this.y = y;
this.z = 0;
this.r = 0;
this.src = 'img/ball.png';
this.gravity = -0.097;
this.scaleX = 1;
this.scaleY = 1;
this.vx = 0;
this.vy = 3.0;
this.vz = 5.0;
this.isLoaded = false;
// update is called inside window.requestAnimationFrame game loop
this.update = function() {
if(this.isLoaded) {
// ball should fly 'into' the scene
this.x += this.vx;
this.y += this.vy;
this.z += this.vz;
// do more stuff like removing it when hit the ground or check for collision
//this.r += ?
this.vz += this.gravity;
}
};
// render is called inside window.requestAnimationFrame game loop after this.update()
this.render = function() {
if(this.isLoaded) {
var x = this.x / this.z;
var y = this.y / this.z;
this.scaleX = this.scaleX / this.z;
this.scaleY = this.scaleY / this.z;
var width = this.img.width * this.scaleX;
var height = this.img.height * this.scaleY;
canvasContext.drawImage(this.img, x, y, width, height);
}
};
// load image
var self = this;
this.img = new Image();
this.img.onLoad = function() {
self.isLoaded = true;
// update offset to spawn the ball in the middle of the click
self.x = this.width/2;
self.y = this.height/2;
// set radius for collision detection because the ball is round
self.r = this.x;
};
this.img.src = this.src;
}
I'm also wondering, which parametes for velocity should be apropriate when rendering the canvas with ~ 60fps using requestAnimationFrame, to have a "natural" flying animation
I'd appreciate it very much, if anyone could point me to the right direction (also with pseudocode explaining the logic ofcourse).
Thanks
I think the best way is to simulate the situation first within metric system.
speed = 30; // 30 meters per second or 108 km/hour -- quite fast ...
angle = 30 * pi/180; // 30 degree angle, moved to radians.
speed_x = speed * cos(angle);
speed_y = speed * sin(angle); // now you have initial direction vector
x_coord = 0;
y_coord = 0; // assuming quadrant 1 of traditional cartesian coordinate system
time_step = 1.0/60.0; // every frame...
// at most 100 meters and while not below ground
while (y_coord > 0 && x_coord < 100) {
x_coord += speed_x * time_step;
y_coord += speed_y * time_step;
speed_y -= 9.81 * time_step; // in one second the speed has changed 9.81m/s
// Final stage: ball shape, mass and viscosity of air causes a counter force
// that is proportional to the speed of the object. This is a funny part:
// just multiply each speed component separately by a factor (< 1.0)
// (You can calculate the actual factor by noticing that there is a limit for speed
// speed == (speed - 9.81 * time_step)*0.99, called _terminal velocity_
// if you know or guesstimate that, you don't need to remember _rho_,
// projected Area or any other terms for the counter force.
speed_x *= 0.99; speed_y *=0.99;
}
Now you'll have a time / position series, which start at 0,0 (you can calculate this with Excel or OpenOffice Calc)
speed_x speed_y position_x position_y time
25,9807687475 14,9999885096 0 0 0
25,72096106 14,6881236245 0,4286826843 0,2448020604 1 / 60
25,4637514494 14,3793773883 0,8530785418 0,4844583502 2 / 60
25,2091139349 14,0737186144 1,2732304407 0,7190203271
...
5,9296028059 -9,0687933774 33,0844238036 0,0565651137 147 / 60
5,8703067779 -9,1399704437 33,1822622499 -0,0957677271 148 / 60
From that sheet one can first estimate the distance of ball hitting ground and time.
They are 33,08 meters and 2.45 seconds (or 148 frames). By continuing the simulation in excel, one also notices that the terminal velocity will be ~58 km/h, which is not much.
Deciding that terminal velocity of 60 m/s or 216 km/h is suitable, a correct decay factor would be 0,9972824054451614.
Now the only remaining task is to decide how long (in meters) the screen will be and multiply the pos_x, pos_y with correct scaling factor. If screen of 1024 pixels would be 32 meters, then each pixel would correspond to 3.125 centimeters. Depending on the application, one may wish to "improve" the reality and make the ball much larger.
EDIT: Another thing is how to project this on 3D. I suggest you make the path generated by the former algorithm (or excel) as a visible object (consisting of line segments), which you will able to rotate & translate.
The origin of the bad behaviour you're seeing is the projection that you use, centered on (0,0), and more generally too simple to look nice.
You need a more complete projection with center, scale, ...
i use that one for adding a little 3d :
projectOnScreen : function(wx,wy,wz) {
var screenX = ... real X size of your canvas here ... ;
var screenY = ... real Y size of your canvas here ... ;
var scale = ... the scale you use between world / screen coordinates ...;
var ZOffset=3000; // the bigger, the less z has effet
var k =ZOffset; // coeficient to have projected point = point for z=0
var zScale =2.0; // the bigger, the more a change in Z will have effect
var worldCenterX=screenX/(2*scale);
var worldCenterY=screenY/(2*scale);
var sizeAt = ig.system.scale*k/(ZOffset+zScale*wz);
return {
x: screenX/2 + sizeAt * (wx-worldCenterX) ,
y: screenY/2 + sizeAt * (wy-worldCenterY) ,
sizeAt : sizeAt
}
}
Obviously you can optimize depending on your game. For instance if resolution and scale don't change you can compute some parameters once, out of that function.
sizeAt is the zoom factor (canvas.scale) you will have to apply to your images.
Edit : for your update/render code, as pointed out in the post of Aki Suihkonen, you need to use a 'dt', the time in between two updates. so if you change later the frame per second (fps) OR if you have a temporary slowdown in the game, you can change the dt and everything still behaves the same.
Equation becomes x+=vx*dt / ... / vx+=gravity*dt;
you should have the speed, and gravity computed relative to screen height, to have same behaviour whatever the screen size.
i would also use a negative z to start with. to have a bigger ball first.
Also i would separate concerns :
- handle loading of the image separatly. Your game should start after all necessary assets are loaded. Some free and tiny frameworks can do a lot for you. just one example : crafty.js, but there are a lot of good ones.
- adjustment relative to the click position and the image size should be done in the render, and x,y are just the mouse coordinates.
var currWidth = this.width *scaleAt, currHeight= this.height*scaleAt;
canvasContext.drawImage(this.img, x-currWidth/2, y-currHeight/2, currWidth, currHeight);
Or you can have the canvas to do the scale. bonus is that you can easily rotate this way :
ctx.save();
ctx.translate(x,y);
ctx.scale(scaleAt, scaleAt); // or scaleAt * worldToScreenScale if you have
// a scaling factor
// ctx.rotate(someAngle); // if you want...
ctx.drawImage(this.img, x-this.width/2, x-this.height/2);
ctx.restore();
I've tried a setInterval loop with css and animate. Both ways of movement consists of tiny movement from oldpos1 -> newpos1 with no random curve movement, easing however occured with jQuery animate but only between randomly generated 1-3 pixels, which is not what I want
.
Does the problem lies in setInterval's clock, where only linear time units flow?
Where should I start, to make below images exist in jQuery?
What I would like to do:
Dodge behaviour:
A, B - particle
AB1 - common dodge area, only certain amount
2 Movement:
Av, Bv - random circular movement
Aacc, Bacc - where the tiny random acceleration occurs (on image marked as more condenced dashed lines)
I would not rely on jQuery's animate for this as your case is rather special ... instead, use the "game loop pattern": Have a game object which keeps a collection of particles, which are moved (and collided ...) and then drawn in regular intervals.
Here's a basic structure:
function Particle(x, y) {
this.x = x;
this.y = y;
this.speed = 0; // in pixels per second
this.direction = 0; // in radians per second
}
Particle.prototype.move = function(d_time) {
this.x += Math.cos(this.direction) * this.speed;
this.y += Math.sin(this.direction) * this.speed;
}
Particle.prototype.draw = function() {
// either set the position of a DOM object belonging to this particle
// or draw to a canvas
}
function Game() {
this.particles = Array();
this.MS_PER_FRAME = 20; // in milliseconds
this.D_TIME = 1000.0 / this.MS_PER_FRAME;
}
Game.prototype.tick = function() {
$.each(this.particles, function(_, particle) {
particle.move(this.D_TIME);
particle.draw();
})
}
Game.prototype.go = function() {
setInterval(this.tick, this.MS_PER_FRAME)
})
Then you can manipulate speed and direction of particles as you like, maybe by introducing additional members d_speed (acceleration) and d_direction or so.