I was "playing" with kripken's box2d when I had a pair of issues. I chose this fork because it seems to be the fastest and the most used.
API defines position on bodyDef but you 'must' give it on body.
Forces, impulses, ... keep attached to the body giving a unexpected constant velocity.
Does anybody get these behaviours before? Does anybody have any hints?
This comes from a complex app but I have simplified for demo:
<html>
<head>
<script src="http://kripken.github.io/box2d.js/box2d.js"></script>
</head>
<body>
<script>
// gravity 0 for top view scene
var world = new Box2D.b2World( new Box2D.b2Vec2(0, 0), true);
var bodyDef = new Box2D.b2BodyDef();
bodyDef.set_type( Box2D.b2_dynamicBody );
bodyDef.set_position(40,40);
var body = world.CreateBody(bodyDef);
// ISSUE 1
// without these two lines real position is 0,0
body.GetPosition().set_x(40);
body.GetPosition().set_y(40);
var dynamicBox = new Box2D.b2PolygonShape();
dynamicBox.SetAsBox(5.0, 5.0);
var fixtureDef = new Box2D.b2FixtureDef();
fixtureDef.set_shape(dynamicBox);
fixtureDef.set_density(1);
fixtureDef.set_friction( 0.8);
fixtureDef.set_restitution( 0.5);
body.CreateFixture(fixtureDef);
//ISSUE 2
// Never ending movements
//body.ApplyLinearImpulse(new Box2D.b2Vec2(5,5),body.GetWorldCenter());
body.ApplyForce(new Box2D.b2Vec2(50,50),body.GetWorldCenter());
function update() {
world.Step(1/30, 10, 10);
world.ClearForces();
console.log(body.GetPosition().get_x()+","+body.GetPosition().get_x());
}
setInterval(update, 1000/60);
</script>
</body>
</html>
For issue 1, set_position should expect a b2Vec2 parameter. Try this:
bodyDef.set_position( new b2Vec2( 40, 40 ) );
I got more issues so finally I switched to box2dweb. Older but more tested and more stable.
Related
I am working on a straightforward double buffering application with HTML5 canvas. (I've commented out the IIFE beginning and end to make debugging easier.) So far as I can tell, when I enter commands live from the developer console, it gives textbook appropriate results for e.g. painting a background image, or scaling and then painting a background image.
I was originally trying to do this as one detail of a tabbed page, but then pulled it out to its own page, which is self-sufficient other than CDN pulls and an uninteresting background image. The code, which duplicates the earlier reported behavior, runs:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>A Snowfall of Skills</title>
<style type="text/css">
canvas#display-background
{
display: none;
}
</style>
</head>
<body>
<canvas id="display-background"></canvas>
<canvas id="display-foreground"></canvas>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.js">
</script>
<script
src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.0/jquery-ui.js">
</script>
<script>
/*
;(jQuery.ready(function()
{
*/
console.log('Reached A');
var original_background_width = jQuery('#display-background'
)[0].width;
var original_background_height =
jQuery('#display-background')[0].height;
var background_width = jQuery(window).width() * .8 - 20;
var background_height = (original_background_height *
background_width / original_background_width);
jQuery('#display-background')[0].width = background_width;
jQuery('#display-background')[0].height = background_height;
jQuery('#display-foreground')[0].width = background_width;
jQuery('#display-foreground')[0].height = background_height;
var raw_background = new Image();
raw_background.src = '/images/skills-snowfall.jpg';
var background_scale = (.8 * jQuery(window).width() /
background_width);
var canvas_height = background_width / background_scale;
var background_canvas = jQuery('#display-background')[0];
var background_context = background_canvas.getContext('2d');
var foreground_canvas = jQuery('#display-foreground')[0];
var foreground_context = foreground_canvas.getContext('2d');
/*
background_context.drawImage(raw_background, 0, 0);
foreground_context.drawImage(raw_background, 0, 0);
*/
background_context.drawImage(raw_background,
0, 0, raw_background.width, raw_background.height,
0, 0, background_canvas.width, background_canvas.height);
foreground_context.drawImage(raw_background,
0, 0, raw_background.width, raw_background.height,
0, 0, foreground_canvas.width, foreground_canvas.height);
console.log('Reached Z');
/*
}()));
*/
</script>
</body>
</html>
The page does appear to be working in one thing; the background canvas is meant not to be directly displayed, although it should be copied to the foreground canvas; and Inspecting HTML elements confirms that there is one displayed canvas, with dimensions making sense.
If I reference raw_background, Chrome shows an IMG element. If I comment out the bracketing, both scaled and unscaled background declarations work correctly when entered manually to the Chrome console. But the context seems to be a noop when it's run live on the page; the Reached A and Reached Z messages are displayed with other diagnostics that are not in my code but appear to me to be innocuous.
What, exactly, am I doing that things basically work dead simple when I enter commands in Chrome's console, but show no visible effect when run as regular JavaScript inlined on a page?
I'm working in a Paper.js project where we're essentially doing image editing. There is one large Raster. I'm attempting to use the getSubRaster method to copy a section of the image (raster) that the user can then move around.
After the raster to edit is loaded, selectArea is called to register these listeners:
var selectArea = function() {
if(paper.project != null) {
var startDragPoint;
paper.project.layers[0].on('mousedown', function(event) { // TODO should be layer 0 in long run? // Capture start of drag selection
if(event.event.ctrlKey && event.event.altKey) {
startDragPoint = new paper.Point(event.point.x + imageWidth/2, (event.point.y + imageHeight/2));
//topLeftPointOfSelectionRectangleCanvasCoordinates = new paper.Point(event.point.x, event.point.y);
}
});
paper.project.layers[0].on('mouseup', function(event) { // TODO should be layer 0 in long run? // Capture end of drag selection
if(event.event.ctrlKey && event.event.altKey) {
var endDragPoint = new paper.Point(event.point.x + imageWidth/2, event.point.y + imageHeight/2);
// Don't know which corner user started dragging from, aggregate the data we have into the leftmost and topmost points for constructing a rectangle
var leftmostX;
if(startDragPoint.x < endDragPoint.x) {
leftmostX = startDragPoint.x;
} else {
leftmostX = endDragPoint.x;
}
var width = Math.abs(startDragPoint.x - endDragPoint.x);
var topmostY;
if(startDragPoint.y < endDragPoint.y) {
topmostY = startDragPoint.y;
} else {
topmostY = endDragPoint.y;
}
var height = Math.abs(startDragPoint.y - endDragPoint.y);
var boundingRectangle = new paper.Rectangle(leftmostX, topmostY, width, height);
console.log(boundingRectangle);
console.log(paper.view.center);
var selectedArea = raster.getSubRaster(boundingRectangle);
var selectedAreaAsDataUrl = selectedArea.toDataURL();
var subImage = new Image(width, height);
subImage.src = selectedAreaAsDataUrl;
subImage.onload = function(event) {
var subRaster = new paper.Raster(subImage);
// Make movable
subRaster.onMouseEnter = movableEvents.showSelected;
subRaster.onMouseDrag = movableEvents.dragItem;
subRaster.onMouseLeave = movableEvents.hideSelected;
};
}
});
}
};
The methods are triggered at the right time and the selection box seems to be the right size. It does indeed render a new raster for me that I can move around, but the contents of the raster are not what I selected. They are close to what I selected but not what I selected. Selecting different areas does not seem to yield different results. The content of the generated subraster always seems to be down and to the right of the actual selection.
Note that as I build the points for the bounding selection rectangle I do some translations. This is because of differences in coordinate systems. The coordinate system where I've drawn the rectangle selection has (0,0) in the center of the image and x increases rightward and y increases downward. But for getSubRaster, we are required to provide the pixel coordinates, per the documentation, which start at (0,0) at the top left of the image and increase going rightward and downward. Consequently, as I build the points, I translate the points to the raster/pixel coordinates by adding imageWidth/2 and imageHeight/2`.
So why does this code select the wrong area? Thanks in advance.
EDIT:
Unfortunately I can't share the image I'm working with because it is sensitive company data. But here is some metadata:
Image Width: 4250 pixels
Image Height: 5500 pixels
Canvas Width: 591 pixels
Canvas Height: 766 pixels
My canvas size varies by the size of the browser window, but those are the parameters I've been testing in. I don't think the canvas dimensions are particularly relevant because I'm doing everything in terms of image pixels. When I capture the event.point.x and event.point.y to the best of my knowledge these are image scaled coordinates, but from a different origin - the center rather than the top left. Unfortunately I can't find any documentation on this. Observe how the coordinates work in this sketch.
I've also been working on a sketch to illustrate the problem of this question. To use it, hold Ctrl + Alt and drag a box on the image. This should trigger some logging data and attempt to get a subraster, but I get an operation insecure error, which I think is because of security settings in the image request header. Using the base 64 string instead of the URL doesn't give the security error, but doesn't do anything. Using that string in the sketch produces a super long URL I can't paste here. But to get that you can download the image (or any image) and convert it here, and put that as the img.src.
The problem is that the mouse events all return points relative to 0, 0 of the canvas. And getSubRaster expects the coordinates to be relative to the 0, 0 of the raster item it is extracting from.
The adjustment needs to be eventpoint - raster.bounds.topLeft. It doesn't really have anything to do with the image width or height. You want to adjust the event points so they are relative to 0, 0 of the raster, and 0, 0 is raster.bounds.topLeft.
When you adjust the event points by 1/2 the image size that causes event points to be offset incorrectly. For the Mona Lisa example, the raster size (image size) is w: 320, h: 491; divided by two they are w: 160, h: 245.5. But bounds.topLeft of the image (when I ran my sketch) was x: 252.5, y: 155.5.
Here's a sketch that shows it working. I've added a little red square highlighting the selected area just to make it easier to compare when it's done. I also didn't include the toDataURL logic as that creates the security issues you mentioned.
Here you go: Sketch
Here's code I put into an HTML file; I noticed that the sketch I put together links to a previous version of the code that doesn't completely work.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Rasters</title>
<script src="./vendor/jquery-2.1.3.js"></script>
<script src="./vendor/paper-0.9.25.js"></script>
</head>
<body>
<main>
<h3>Raster Bug</h3>
<div>
<canvas id="canvas"></canvas>
</div>
<div id="position">
</div>
</main>
<script>
// initialization code
$(function() {
// setup paper
$("#canvas").attr({width: 600, height: 600});
var canvas = document.getElementById("canvas");
paper.setup(canvas);
// show a border to make range of canvas clear
var border = new paper.Path.Rectangle({
rectangle: {point: [0, 0], size: paper.view.size},
strokeColor: 'black',
strokeWidth: 2
});
var tool = new paper.Tool();
// setup mouse position tracking
tool.on('mousemove', function(e) {
$("#position").text("mouse: " + e.point);
});
// load the image from a dataURL to avoid CORS issues
var raster = new paper.Raster(dataURL);
raster.position = paper.view.center;
var lt = raster.bounds.topLeft;
var startDrag, endDrag;
console.log('rb', raster.bounds);
console.log('lt', lt);
// setup mouse handling
tool.on('mousedown', function(e) {
startDrag = new paper.Point(e.point);
console.log('sd', startDrag);
});
tool.on('mousedrag', function(e) {
var show = new paper.Path.Rectangle({
from: startDrag,
to: e.point,
strokeColor: 'red',
strokeWidth: 1
});
show.removeOn({
drag: true,
up: true
});
});
tool.on('mouseup', function(e) {
endDrag = new paper.Point(e.point);
console.log('ed', endDrag);
var bounds = new paper.Rectangle({
from: startDrag.subtract(lt),
to: endDrag.subtract(lt)
});
console.log('bounds', bounds);
var sub = raster.getSubRaster(bounds);
sub.bringToFront();
var subData = sub.toDataURL();
sub.remove();
var subRaster = new paper.Raster(subData);
subRaster.position = paper.view.center;
});
});
var dataURL = ; // insert data or real URL here
</script>
</body>
</html>
I know there are question like this but I still can't find a fix, I've looked through the console and can't see anything. There are two images, one which should appear on set coordinates, and another which follows the mouse, the one which is mean't to follow the mouse does not show up but the other one does.
Main.js
/**
* Created with JetBrains WebStorm.
* User: Script47
* Date: 22/09/13
* Time: 00:54
*/
function drawAvatars() {
// Create variable for the canvas & create a new object for image
var gameCanvas = document.getElementById("gameCanvas");
var userImage = new Image();
// The source of the images
userImage.src = ("Images/userImage.png");
// Create an event listener then call function redrawAvatar
gameCanvas.addEventListener("mousemove", redrawAvatar);
}
function redrawAvatar(mouseEvent) {
var gameCanvas = document.getElementById("gameCanvas");
var userImage = new Image();
var enemyImage = new Image();
var score = 0;
userImage.src = ("Images/userImage.png");
enemyImage.src = ("Images/enemyImage.png");
// Erase canvas sort of refresh, then re-draw image following the coordinates of the mouse in the canvas
gameCanvas.width = 400;
gameCanvas.getContext("2d").drawImage(userImage, mouseEvent.offsetX, mouseEvent.offsetY);
gameCanvas.getContext("2d").drawImage(enemyImage, 150, 150);
// Simple hit detection to see if user image hits enemy image
if (mouseEvent.offsetX > 130 && mouseEvent.offsetX < 175 && mouseEvent.offsetY > 130 && mouseEvent.offsetY < 175) {
score++;
alert("You hit the enemy!\n You score is: " +score);
}
}
Index.html
<!DOCTYPE html>
<html>
<head>
<title>Avoid Me | Game</title>
<link rel="stylesheet" type="text/css" href="CSS/styles.css">
<script src="JS/Main.js"></script>
</head>
<body>
<br/>
<center><h3>Avoid Me!</h3>
<br/>
<br/>
<canvas id="gameCanvas" height="300" width="400" onclick="drawAvatars();">
<p><strong>Notice:</strong> Browser does not support canvas!</p>
</canvas>
</center>
</body>
</html>
JsFiddle
(Basing this off the code in the link you provided.)
In your mouseMovement function, you use mouseEvent.offsetX and mouseEvent.offsetY to get the player's position, but Firefox unfortunately doesn't support those properties. Unfortunately, IIRC, the only thing that works cross-browser is to get the position of the canvas, and subtract it from the event's pageX/pageY properties. You can use the canvas's getBoundingClientRect() method to find its position on the page.
This is is an example of a version of the function that should work in Firefox as well:
function mouseMovement(mouseEvent) {
var canvasPosition = gameCanvas.getBoundingClientRect();
userImageX = mouseEvent.pageX - canvasPosition.left;
userImageY = mouseEvent.pageY - canvasPosition.top;
}
I'm going to start this question off by saying that this is 100% working in Firefox (v21.0). For some reason it's not working in Google Chrome (v27.0.1453.94m). It also doesn't work in IE10.
Here is the JavaScript code I'm having issues with:
function canvasDrawBackground(value){
console.log(value);
stage.removeChild(background);
var temp = new createjs.Bitmap("images/bg_" + value +".jpg");
background = new createjs.Container();
background.x = background.y = 0;
background.addChild(temp);
stage.addChild(background);
background.addEventListener("mousedown", function(evt) {
var offset = {x:evt.target.x-evt.stageX, y:evt.target.y-evt.stageY};
evt.addEventListener("mousemove",function(ev) {
ev.target.x = ev.stageX+offset.x;
ev.target.y = ev.stageY+offset.y;
stage.update();
});
});
stage.update();
}
So, in Firefox the above code works, as in the image is added to the canvas and you can drag it around.
In Chrome / IE10 nothing happens - or more simply nothing appears on the canvas. I think the issue is in regards to when I add the image into the container, as I can place other items into the container and it works.
I am using http://code.createjs.com/easeljs-0.6.1.min.js and this code is based off of the "drag" tutorial. Here's the code from the tutorial:
<!DOCTYPE html>
<html>
<head>
<title>EaselJS demo: Dragging</title>
<link href="../../shared/demo.css" rel="stylesheet" type="text/css">
<script src="http://code.createjs.com/easeljs-0.6.0.min.js"></script>
<script>
var stage, output;
function init() {
stage = new createjs.Stage("demoCanvas");
// this lets our drag continue to track the mouse even when it leaves the canvas:
// play with commenting this out to see the difference.
stage.mouseMoveOutside = true;
var circle = new createjs.Shape();
circle.graphics.beginFill("red").drawCircle(0, 0, 50);
var label = new createjs.Text("drag me", "bold 14px Arial", "#FFFFFF");
label.textAlign = "center";
label.y = -7;
var dragger = new createjs.Container();
dragger.x = dragger.y = 100;
dragger.addChild(circle, label);
stage.addChild(dragger);
dragger.addEventListener("mousedown", function(evt) {
var offset = {x:evt.target.x-evt.stageX, y:evt.target.y-evt.stageY};
// add a handler to the event object's onMouseMove callback
// this will be active until the user releases the mouse button:
evt.addEventListener("mousemove",function(ev) {
ev.target.x = ev.stageX+offset.x;
ev.target.y = ev.stageY+offset.y;
stage.update();
});
});
stage.update();
}
</script>
</head>
<body onLoad="init();">
<canvas id="demoCanvas" width="500" height="200">
alternate content
</canvas>
</body>
</html>
To simulate my issue, change "var circle = new createjs.Shape();" into a bitmap / image, createjs.Bitmap("images/bg_" + value +".jpg");. It then doesn't render.
Any help is greatly appreciated! Hopefully I'm just doing it wrong. :P
This is probably because the image is not loaded. If you only update the stage after creating it, the image may not display. I would recommend adding a callback to the image to update the stage after its loaded.
// Simple approach. May not work depending on the scope of the stage.
var temp = new createjs.Bitmap("images/bg_" + value +".jpg");
temp.image.onload = function() { stage.update(); }
It also may make sense to preload the images you intend to use.
I'm starting to program a javascript tower defense; so far i have the movement of the minions over a trajectory. But I have a very big trouble, the game suddenly freezes for a couple of seconds. I'm guessing that is the garbage collector doing its job, any ideas on how i can solve this will be very good since i plan on adding a lot more of elements to the game and i don't want to keep coding till i get this flowing perfectly!
The code so far is pretty simple; you can check it out here
Here's the code:
<html>
<head>
<style>
#game{
background:red;
width:500px;
height:500px;
position:relative;
}
.mostro {
background:black;
width:15px;
height:15px;
position:absolute;
}
</style>
</head>
<body>
<div id="game">
<script type="text/javascript">
waypoint_x = [40, 140, 140, 220, 220, 80, 80, 340, 340, 420, 420];
waypoint_y = [140, 140, 60, 60, 240, 240, 320, 320, 100, 100, -20];
delay = 25;
new_monster = 0;
monsters_placed = 0;
monsters = [];
var d = new Date();
dist_x = 0;
dist_y = 0;
angle = 0;
mostro="";
total_monsters = 5;
function runGame() {
if (monsters_placed<total_monsters) {
new_monster++;
}
if (new_monster == delay) {
new_monster = 0;
document.getElementById("game").innerHTML = document.getElementById("game").innerHTML + '<div class="mostro" id="mostro-'+monsters_placed+'"></div>';
monsters_placed++;
}
for (i=0;i<monsters_placed;i=i+1) {
mostro = monsters[i];
dist_x = waypoint_x[mostro.point_to_reach] - mostro._x;
dist_y = waypoint_y[mostro.point_to_reach] - mostro._y;
if ((Math.abs(dist_x) + Math.abs(dist_y)) < 1) {
monsters[i].point_to_reach++;
}
angle = Math.atan2(dist_y, dist_x);
mostro._x = mostro._x + mostro.speed * Math.cos(angle);
mostro._y = mostro._y + mostro.speed * Math.sin(angle);
monsters[i]._rotation = angle/Math.PI*180-90
document.getElementById("mostro-"+i).style.left = Math.ceil(mostro._x) + "px";
document.getElementById("mostro-"+i).style.top = Math.ceil(mostro._y) + "px";
}
}
function setUpGame(){
for(i=0;i<=total_monsters;i++){
monsters[i] = new Object();
monsters[i].point_to_reach = 0;
monsters[i].speed = 1;
monsters[i]._x = 0;
monsters[i]._y = 0;
}
}
setUpGame();
setInterval(runGame,10);
</script>
</body>
</html>
Its not the garbage collector doing the job but in your code when you try to set the top and left positions, at a particuar time the value that you try to set in not a number. So the code breaks....
I think this occurs when the moving div crosses the top of the container with red background.
Yes, thats right: the delay is because when there are too many monsters, there are too many position updates that need to be done. This causes the "redraw" delay..
I see that there is a DOM element for each monster(as should be the case). But, you are updating their positions one by one.
Tips to reduce this lag:
Firstly, it would be a better stategy to update their positions en masse:
<div id='monster-container'>
<div id='monstser-1'></div>
<div id='monstser-2'></div>
<div id='monstser-3'></div>
</div>
So update the position of '#monster-container' when the monsters move. This way redraw time will definitely be minimized. What I say is from a primitive understanding of your game. You may need to modify this approach depending upon the path of the monsters. My approach will work directly only if the monsters only move in a straight line.
Secondly, if you are using img's for the monsters, consider using div's, and set the images as backgrounds of the div. This has given faster redraw performance in many of my pet games.
Thirdly, if you are using individual images for the monsters, consider using a composite image and CSS spriting.
Wish you luck with your game! Cheers!!
jrh
Yes, that's definitely a garbage collector. I am developing a JavaScript game myself, and I spent last few days trying to get rid of this problem. So far I can say that it's impossible.
However, I would like to note that different browsers have different garbage collectors, and for example, in Safari 4, your example runs perfectly smooth.
And here is interesting link on this topic: Reducing freezing with Object Pooling
Honestly, I think, that technique described in that article is not very helpful, because even in your example, that doesn't have any variables needed to be cleared, freezing is really noticeable.
Also I've rewritten your example, to test if global variables ruined performance.
You can see the difference yourself