Pixi.js images jump when dragged - pixi.js

I am having trouble with pixi.js I am creating a page like http://www.wolverineunleashed.com/#muscles I have created a big stage which the user can use their drag their way around, it all works fine apart from when the user is dragging, the images shake on the screen, I am thinking that it might be a rendering issue? But at the moment I am pulling my hair out so any help would be most grateful. The code I have is:
var w = window.innerWidth;
var h = window.innerHeight;
var images = [];
var stage = new PIXI.Container();
var renderer = new PIXI.autoDetectRenderer(w, h,{transparent:true},true);
document.body.appendChild(renderer.view);
var background = new PIXI.Container();
background.parent = background;
background.interactive = true;
background.on('mousedown', onDragStart)
.on('touchstart', onDragStart)
.on('mouseup', onDragEnd)
.on('mouseupoutside', onDragEnd)
.on('touchend', onDragEnd)
.on('touchendoutside', onDragEnd)
.on('mousemove', onDragMove)
.on('touchmove', onDragMove);
loadImages();
requestAnimationFrame(animate);
function animate() {
requestAnimationFrame(animate);
renderer.render(background);
}
function onDragStart(event)
{
this.data = event.data;
this.mousePressPoint = [];
this.dragging = true;
this.mousePressPoint[0] = this.data.getLocalPosition(this.parent).x - this.position.x;
this.mousePressPoint[1] = this.data.getLocalPosition(this.parent).y - this.position.y;
}
function onDragEnd()
{
this.dragging = false;
this.data = null;
}
function onDragMove()
{
if (this.dragging)
{
var position = this.data.getLocalPosition(this.parent);
this.position.x = parseInt(position.x - this.mousePressPoint[0]);
this.position.y = parseInt(position.y - this.mousePressPoint[1]);
}
}

This is a very old question, and the PIXI library has changed since the OP, but I was having problems with this myself, and I don't feel the question yet has a good answer...
Some of the PIXI API has changed since the OP, but see the PIXI documentation for the dragging bunnies example.
The jumpiness of the sprite when it first starts to drag is because the sprite is always positioned relative to the anchor/pivot point. Because of this, the position of the anchor point follows the position of the mouse when dragging (at least in the code above). If you click in the center of the bunnies, you don't notice, but clicking on the very edge, produces a very noticeable jump. When using significantly larger sprites (as in the example linked in the OP), this jump can become quite glaring.
Here is how I fixed it:
Very little needs to be changed from the PIXI demo. onDragEnd and onDragMove remain identical:
function onDragEnd(){
this.dragging=false;
this.data = null;
}
function onDragMove(){
if (this.dragging){
let newPosition = this.data.getLocalPosition(this.parent);
this.x = newPosition.x;
this.y = newPosition.y;
}
}
However, we need to update the pivot point to the location of the click event within the onDragStart function like so:
function onDragStart(event){
this.data = event.data;
//store this variable for convenience
let position = this.data.getLocalPosition(this);
// Set the pivot point to the new position
this.pivot.set(position.x, position.y)
// update the new position of the sprite to the position obtained through
// the global data. This ensures the position lines up with the location of
// the mouse on the screen. I'm not certain why, but this is necessary.
this.position.set(this.data.global.x, this.data.global.y)
this.dragging = true;
}
With this set up, dragging sprites will be smooth, regardless of size. Excellent for creating a click-and-drag-to-explore kind of environment, as linked in the OP.
Hope this helps someone else two years in the future.

I do hope that you have already figured out how to fix this issue, but since there is no answer on here, I'm going to guide you in the right direction.
The code you give us is missing the loadImages() function, but I believe that we should be able to solve it anyway.
The problem seems to lie in this code snippet:
var background = new PIXI.Container();
background.parent = background;
background.interactive = true;
background.on('mousedown', onDragStart)
.on('touchstart', onDragStart)
.on('mouseup', onDragEnd)
.on('mouseupoutside', onDragEnd)
.on('touchend', onDragEnd)
.on('touchendoutside', onDragEnd)
.on('mousemove', onDragMove)
.on('touchmove', onDragMove);
Here you are making the background container interactive and giving it all the event handlers.
What you should be doing instead is making each individual sprite/image interactive and giving them the eventhandlers that move it around.
I added the following code to your code:
// Create a sprite from some image
var sprite = new PIXI.Sprite.fromImage('some_image.png');
// Make the sprite interactive. should be done to each individual sprite
sprite.interactive = true;
// Set the anchor in the center of our sprite
sprite.anchor.x = 0.5;
sprite.anchor.y = 0.5;
// Position our sprite in the center of the renderer
sprite.position.x = renderer.width / 2;
sprite.position.y = renderer.height / 2;
sprite.on('mousedown', onDragStart)
.on('touchstart', onDragStart)
.on('mouseup', onDragEnd)
.on('mouseupoutside', onDragEnd)
.on('touchend', onDragEnd)
.on('touchendoutside', onDragEnd)
.on('mousemove', onDragMove)
.on('touchmove', onDragMove);
background.addChild(sprite);
And you should put this inside your loadImages function. And then make that function iterate through each image, giving them the event handlers and options they need for this to work.
Here is the code, based on yours, that works.
var w = window.innerWidth;
var h = window.innerHeight;
var images = [];
var stage = new PIXI.Container();
var renderer = new PIXI.autoDetectRenderer(w, h,{transparent:true},true);
document.body.appendChild(renderer.view);
var background = new PIXI.Container();
// Create a sprite from some image
var sprite = new PIXI.Sprite.fromImage('some_image.png');
// Make the sprite interactive. should be done to each individual sprite
sprite.interactive = true;
// Set the anchor in the center of our sprite
sprite.anchor.x = 0.5;
sprite.anchor.y = 0.5;
// Position our sprite in the center of the renderer
sprite.position.x = renderer.width / 2;
sprite.position.y = renderer.height / 2;
sprite.on('mousedown', onDragStart)
.on('touchstart', onDragStart)
.on('mouseup', onDragEnd)
.on('mouseupoutside', onDragEnd)
.on('touchend', onDragEnd)
.on('touchendoutside', onDragEnd)
.on('mousemove', onDragMove)
.on('touchmove', onDragMove);
background.addChild(sprite);
requestAnimationFrame(animate);
function animate() {
requestAnimationFrame(animate);
renderer.render(background);
}
function onDragStart(event)
{
this.data = event.data;
this.mousePressPoint = [];
this.dragging = true;
}
function onDragEnd()
{
this.dragging = false;
this.data = null;
}
function onDragMove()
{
if (this.dragging)
{
var position = this.data.getLocalPosition(this.parent);
this.position.x = position.x;
this.position.y = position.y;
}
}

Related

Maintaining object size AND position while zooming in fabric js

I was trying to maintain the object size while zooming, i tried to get inspired by this answer in which the guy who wrote it didn't solve the controls issue in such as case, as a consequence you can see them not sticking to the object while zooming as in this screenshot.
But i came with this solution to maintain the object position and controls by updating its left and top after calculating them based on the inverted viewportTransform by calculating a new fabric.Point using the fabric.util.transformPoint function
fabric.Object.prototype.transform = function(ctx) {
const obj = this;
const {
ignoreZoom,
group,
canvas,
left,
top
} = obj;
const {
contextTop,
viewportTransform,
getZoom,
requestRenderAll,
} = canvas;
var needFullTransform = (group && !group._transformDone) || (group && canvas && ctx === contextTop);
if (ignoreZoom) {
const oldP = new fabric.Point(left, top);
const newP = fabric.util.transformPoint(oldP, fabric.util.invertTransform(viewportTransform));
var zoom = 1 / getZoom();
/* // here i tried to refresh the whole canvas with requestRenderAll()
this.set({
left: newP.x,
top: newP.y,
scaleX: zoom,
scaleY: zoom,
});
this.setCoords();
requestRenderAll();
*/
// but here i try refresh the object only which is better i think
this.left = newP.x;
this.top = newP.y;
this.scaleX = zoom;
this.scaleY = zoom;
this.drawObject(ctx);
}
var m = this.calcTransformMatrix(!needFullTransform);
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
}
I have made this codesandbox as a demo for my code. As you can see in this screenshot, controls stick around the object but the whole of them doesn't maintain their position relatively to the background and sometimes they disappear completely.
I need the object to keep its position relatively to the background.
How to make it better ?
// EDIT
I tried to understand better what happens while zooming, i found the fabric.Canvas.zoomToPoint() which is used for zooming (as in their tutorial)
zoomToPoint: function (point, value) {
// TODO: just change the scale, preserve other transformations
var before = point, vpt = this.viewportTransform.slice(0);
point = transformPoint(point, invertTransform(this.viewportTransform));
vpt[0] = value;
vpt[3] = value;
var after = transformPoint(point, vpt);
vpt[4] += before.x - after.x;
vpt[5] += before.y - after.y;
return this.setViewportTransform(vpt);
},
i guess the best way to fix the object position relatively to the background will be to apply the inverse transformation of the one applied to the canvas for the zoom to the object.
So i wrote this function
function getNewVpt(point, value) {
var before = point,
vpt = canvas.viewportTransform.slice(0);
point = fabric.util.transformPoint(point, fabric.util.invertTransform(canvas.viewportTransform));
vpt[0] = value;
vpt[3] = value;
var after = fabric.util.transformPoint(point, vpt);
vpt[4] += before.x - after.x;
vpt[5] += before.y - after.y;
return vpt;
}
and i used it to rewrite the fabric.Object.prototype.transform
fabric.Object.prototype.transform = function (ctx) {
const obj = this;
const { ignoreZoom, group, canvas: objCanvas, left, top } = obj;
const {
contextTop,
viewportTransform,
} = objCanvas;
var needFullTransform =
(group && !group._transformDone) ||
(group && objCanvas && ctx === contextTop);
if (ignoreZoom && zoomingIsOn) {
zoomingIsOn = false;
var zoom = 1 / objCanvas.getZoom();
const oldP = new fabric.Point(left, top);
console.log('transform : oldP : ', oldP);
const newVpt = getNewVpt(oldP, zoom)
const newP = fabric.util.transformPoint(oldP, newVpt);
console.log('transform : newP : ', newP);
// here i tried to refresh the whole canvas with requestRenderAll()
this.set({
left: newP.x,
top: newP.y,
scaleX: zoom,
scaleY: zoom
});
this.setCoords();
console.log('transform : CALLING objCanvas.requestRenderAll() ');
objCanvas.requestRenderAll();
// but here i try refresh the object only which is better i think
// this.left = newP.x;
// this.top = newP.y;
// this.scaleX = zoom;
// this.scaleY = zoom;
// this.drawObject(ctx);
}
var m = this.calcTransformMatrix(!needFullTransform);
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
};
And here i forked this new codesandbox for this second solution , the result seems to be better than the former solution but it still not perfect. What i may still be doing wrong ?!
// EDIT 2
I tried to pass objCanvas.getZoom() instead of zoom as second parameter to the getNewVpt() function. It seems there is some more improovement but still not perfect again
// Edit 3
In This codesandbox probably i got the best result i could get using another function which returns directly the new point:
function getNewPt(point, value) {
// TODO: just change the scale, preserve other transformations
var vpt = canvas.viewportTransform.slice(0);
point = fabric.util.transformPoint(point, fabric.util.invertTransform(canvas.viewportTransform));
vpt[0] = value;
vpt[3] = value;
return fabric.util.transformPoint(point, vpt);;
}
I still wish anybody who can tell me if there is a way to improove it more. As you can see the triangle returns back to its initial position after zooming/ dezooming and getting back to the same initial zoom value which is good but between those initial and final states , it still seems not to be in the right spot..
You just have to call zoomToPoint where it zooms and the objects will keep their position and scale relative to the background.
Try the following
canvas.on('mouse:wheel', function(opt) {
// console.log(opt.e.deltaY)
let zoomLevel = canvas.getZoom();
// console.log('zoom Level: ', (zoomLevel * 100).toFixed(0), '%');
zoomLevel += opt.e.deltaY * -0.01;
// Restrict scale
zoomLevel = Math.min(Math.max(.125, zoomLevel), 20);
canvas.zoomToPoint(
new fabric.Point(opt.e.offsetX, opt.e.offsetY),
zoomLevel,
);
canvas.renderAll();
})

PIXI getting image width,height

I need to get size of image that I just added to stage and set it to defaultX and defaultY before I call the autorenderer, but I can't seem to grab the size.
How do I grab the size?
Also, from what I got so far, I think the best way to set the size is including with autorenderer. But what would be the implication here if I resize() later?
Any help will be appreciated. Thank you.
my code:
this.stage = new PIXI.Container();
var texture = PIXI.Texture.fromImage('background.png'); //1610x640
var spr = new PIXI.Sprite(texture);
this.stage.addChild(spr);
defaultX = texture.width;
defaultY = texture.height;
this.renderer = PIXI.autoDetectRenderer(defaultX, defaultY); //640x690
this.renderer.resize(x,y); // I know this works with other values
// eg if I hardcode with 1610,640
// but texture.width doesn't work and
// I don't want to hardcode it
Your image can not be loaded when you call texture.width.
I use next construction:
this.stage = new PIXI.Container();
this.renderer = PIXI.autoDetectRenderer(640, 690); //640x690
var baseTexture = new PIXI.BaseTexture('background.png');
// can be loaded from cache
if (baseTexture .hasLoaded) {
createSprite(baseTexture);
} else {
baseTexture .on('loaded', function() {
createSprite(baseTexture); // or this
});
}
function createSprite(baseTexture) {
var texture = new PIXI.Texture(baseTexture);
var spr = new PIXI.Sprite(texture);
this.stage.addChild(spr);
defaultX = texture.width;
defaultY = texture.height;
this.renderer.resize(defaultX,defaultY);
}

How to properly delete a box2d body in version: Box2dWeb-2.1.a.3, Box2D_v2.3.1r3? Box2D bug?

Update
Since the problem has been found I've also find out that Box2D for web is leaking on every side :/
To show this I made a simple circle moving in a static polygon and here is the result after some time.
Notice how the following items are leaking as I'm not creating any body or changing the world in any way:
b2Vec2
Features
b2ManifoldPoint
b2ContactID
b2Manifold
b2ContactEdge
b2PolyAndCircleContact
Array
...
Original post
I have a problem because I'm profiling my game and the garbage collector doesnt' delete my bodies, contacts and other stuff. Then I've looked at what are they keeping from the GC and was the Box2D itself. This might lead to 2 options: I'm doing it bad or Box2D is leaking. I consider is my cause.
What exactly is keeping it?
contact.m_nodeA.other was appearing to be the most used to keep it from GC.
other times: m_fixtureB in a contact... see image
You can see that the body has a __destroyed property. That is set manually before deleting it with world.DestroyBody(body)
When I destroy a body I call it after I call the step method on the world.
As you can see from the box2d method it doesn't get rid of the other variable nor it changes it to another body and my body is not GC.
Any idea of what I'm missing here?
Now I can fix the problem only if the world.Step is not ran:
var gravity = new Box2D.Vec2(0, 0);
var doSleep = true;
var world = new Box2D.World(gravity, doSleep);
var step = false;
var fixtureDef = new Box2D.FixtureDef();
fixtureDef.density = 1.0;
fixtureDef.friction = 0.5;
fixtureDef.restitution = 0.2;
fixtureDef.shape = new Box2D.PolygonShape();
fixtureDef.shape.SetAsBox(1, 1);
var bodyDef = new Box2D.BodyDef;
bodyDef.type = Box2D.Body.b2_dynamicBody;
bodyDef.position.x = 0.4;
bodyDef.position.y = 0.4;
var bodies = []
var fix = [];
window.c = function(){
for(var i = 0; i < 100; i++){
var body = world.CreateBody(bodyDef);
body._id = i;
fix.push(body.CreateFixture(fixtureDef));
bodies.push(body);
}
if(step){world.Step(1/60, 3, 3); world.ClearForces();}
console.log('Created', bodies)
fixtureDef = null;
bodyDef = null;
}
window.d = function(){
_.each(bodies, function(body, i){
body.DestroyFixture(fix[i]);
world.DestroyBody(body);
fix[i] = null;
bodies[i] = null;
})
if(step){world.Step(1/60, 3, 3); world.ClearForces();}
bodies = null;
fix = null;
}
Change the step to true and the memory leak problem appears again.
Reproduce the memory leak problem:
Code in your file:
var gravity = new Box2D.Vec2(0, 0);
var doSleep = true;
var world = new Box2D.World(gravity, doSleep);
var bodies = []
window.c = function(){
for(var i = 0; i < 100; i++){
var bodyDef = new Box2D.BodyDef();
bodyDef.type = 2;
var shape = new Box2D.PolygonShape();
shape.SetAsBox(1, 1);
var fixtureDef = new Box2D.FixtureDef();
fixtureDef.shape = shape;
var body = world.CreateBody(bodyDef);
body._id = i;
body.CreateFixture(fixtureDef);
bodies.push(body);
}
world.Step(0.3, 3, 3);
console.log('Created', bodies)
}
window.d = function(){
_.each(bodies, function(body, i){
world.DestroyBody(body);
bodies[i] = null;
})
world.Step(0.3, 3, 3);
bodies = null;
}
Open google chrome:
Then open your profile and make a snapshot.
Now run the c() method in your console to create 100 bodies
Now snapshot 2
Search in snapshot for b2Body and you'll find 100 Object count
Now run d() to delete all your bodies;
Force Garbage collection by clicking on the garbage can
Make a snapshot 3
Search for b2Body and you'll also find 100 Object count
At the last step should only be 0 objects as they have been destroyed. Instead of this you'll find this:
Now you can see there are a lot of references from b2ContactEdge. Now if you remove the world.Step part of the code you will only see 2 references to the body.
If you remove this line
body.CreateFixture(fixtureDef);
or making the body static is not leaking anymore.
My game loop
...gameLoop = function(o){
// used a lot here
var world = o.world;
// calculate the new positions
var worldStepSeconds = o.worldStepMs / 1000;
// step world
world.Step(worldStepSeconds, o.velocityIterations, o.positionIterations)
// render debug
if(o.renderDebug){
world.DrawDebugData();
}
// always to not accumulate forces, maybe some bug occurs
world.ClearForces();
// tick all ticking entities
_.each(o.getTickEntitiesFn(), function(actor){
if(!actor) return;
actor.tick(o.worldStepMs, o.lastFrameMs);
})
// update PIXI entities
var body = world.GetBodyList();
var worldScale = world.SCALE;
var destroyBody = world.DestroyBody.bind(world);
while(body){
var actor = null;
var visualEntity = null;
var box2DEntity = o.getBox2DEntityByIdFn(body.GetUserData());
if(box2DEntity){
visualEntity = o.getVisualEntityByIdFn(box2DEntity.getVisualEntityId());
if(box2DEntity.isDestroying()){
// optimization
body.__destroyed = true;
world.DestroyBody(body);
box2DEntity.completeDestroy();
}
}
if(visualEntity){
if(visualEntity.isDestroying()){
visualEntity.completeDestroy();
}else{
var inverseY = true;
var bodyDetails = Utils.getScreenPositionAndRotationOfBody(world, body, inverseY);
visualEntity.updateSprite(bodyDetails.x, bodyDetails.y, bodyDetails.rotation);
}
}
// this delegates out functionality for each body processed
if(o.triggersFn.eachBody) o.triggersFn.eachBody(world, body, visualEntity);
body = body.GetNext();
}
// when a joint is created is then also created it's visual counterpart and then set to userData.
var joint = world.GetJointList();
while(joint){
var pixiGraphics = joint.GetUserData();
if(pixiGraphics){
// In order to draw a distance joint we need to know the start and end positions.
// The joint saves the global (yes) anchor positions for each body.
// After that we need to scale to our screen and invert y axis.
var anchorA = joint.GetAnchorA();
var anchorB = joint.GetAnchorB();
var screenPositionA = anchorA.Copy();
var screenPositionB = anchorB.Copy();
// scale
screenPositionA.Multiply(world.SCALE);
screenPositionB.Multiply(world.SCALE);
// invert y
screenPositionA.y = world.CANVAS_HEIGHT - screenPositionA.y
screenPositionB.y = world.CANVAS_HEIGHT - screenPositionB.y
// draw a black line
pixiGraphics.clear();
pixiGraphics.lineStyle(1, 0x000000, 0.7);
pixiGraphics.moveTo(screenPositionA.x, screenPositionA.y);
pixiGraphics.lineTo(screenPositionB.x, screenPositionB.y);
}
joint = joint.GetNext();
}
// render the PIXI scene
if(o.renderPixi){
o.renderer.render(o.stage)
}
// render next frame
requestAnimFrame(o.requestAnimFrameFn);
}
Code from Box2d:
b2ContactManager.prototype.Destroy = function (c) {
var fixtureA = c.GetFixtureA();
var fixtureB = c.GetFixtureB();
var bodyA = fixtureA.GetBody();
var bodyB = fixtureB.GetBody();
if (c.IsTouching()) {
this.m_contactListener.EndContact(c);
}
if (c.m_prev) {
c.m_prev.m_next = c.m_next;
}
if (c.m_next) {
c.m_next.m_prev = c.m_prev;
}
if (c == this.m_world.m_contactList) {
this.m_world.m_contactList = c.m_next;
}
if (c.m_nodeA.prev) {
c.m_nodeA.prev.next = c.m_nodeA.next;
}
if (c.m_nodeA.next) {
c.m_nodeA.next.prev = c.m_nodeA.prev;
}
if (c.m_nodeA == bodyA.m_contactList) {
bodyA.m_contactList = c.m_nodeA.next;
}
if (c.m_nodeB.prev) {
c.m_nodeB.prev.next = c.m_nodeB.next;
}
if (c.m_nodeB.next) {
c.m_nodeB.next.prev = c.m_nodeB.prev;
}
if (c.m_nodeB == bodyB.m_contactList) {
bodyB.m_contactList = c.m_nodeB.next;
}
this.m_contactFactory.Destroy(c);
--this.m_contactCount;
}
b2ContactFactory.prototype.Destroy = function (contact) {
if (contact.m_manifold.m_pointCount > 0) {
contact.m_fixtureA.m_body.SetAwake(true);
contact.m_fixtureB.m_body.SetAwake(true);
}
var type1 = parseInt(contact.m_fixtureA.GetType());
var type2 = parseInt(contact.m_fixtureB.GetType());
var reg = this.m_registers[type1][type2];
if (true) {
reg.poolCount++;
contact.m_next = reg.pool;
reg.pool = contact;
}
var destroyFcn = reg.destroyFcn;
destroyFcn(contact, this.m_allocator);
}
I have the same problem, but I think I find out from where it comes.
Instead of m_* try functions, like GetFixtureA() instead of m_fixtureA.
Totti did you ever figure this out? It looks like box2dweb requires manual destruction and memory management.
I think I have found your leaks, un-implemented ( static class ) destruction functions:
b2Joint.Destroy = function (joint, allocator) {}
b2CircleContact.Destroy = function (contact, allocator) {}<
b2PolygonContact.Destroy = function (contact, allocator) {}
b2EdgeAndCircleContact.Destroy = function (contact, allocator) {}<
b2PolyAndCircleContact.Destroy = function (contact, allocator) {}
b2PolyAndEdgeContact.Destroy = function (contact, allocator) {}
[UPDATE...]
b2DestructionListener.b2DestructionListener = function () {};
b2DestructionListener.prototype.SayGoodbyeJoint = function (joint) {}
b2DestructionListener.prototype.SayGoodbyeFixture = function (fixture) {}
b2Contact.prototype.Reset(fixtureA, fixtureB)
called with with one/both fixture arguments resets passed in fixture/s BUT ALSO pass in NO arguments and it 'nulls' all the the b2Contact properties! (UNTESTED:) but I suggest set your YOURcontactListener class up to handle all contact callbacks EVERY call with Reset(??) dynamically configureable as logic requies EVERY call (there are more than you'd imagine each and every world step).
Also take Colt McAnlis clever advice and strategically pre allocate all the memory the life of your game will need (by creating game and box2d object pools now you know objects can be reset) so the garbage collector NEVER runs until, you destroy object pools at times of your own convenience.... i.e when you close the tab, or your device needs recharging! ;D [...UPDATE]
// you can define and assign your own contact listener ...via...
YOUR.b2world.b2ContactManager.m_world.m_contactList = new YOURcontactlistener();<br>[edit]...if you dont it actually does have Box2D.Dynamics.b2ContactListener.b2_defaultListener.
// box2d in the worldStep calls YOURcontactlistener.update() via:
this.b2world.b2ContactManager.m_world.m_contactList.Update(this.m_contactListener) // this.m_contactListener being YOURS || b2_defaultListener;
// which instantiates ALL your listed leaking object like so:
{b2Contact which instantiates {b2ContactEdge} and {b2Manifold which instantiates {b2ManifoldPoint{which instantiates m_id.key == ContactID{which instantiates Features}}}} along with {B2Vec2} are instantiated in b2ContactResult ...which I can not actually find but assume it must be instantiated in the Solver.
// There is a Contacts.destroyFcn callback is CREATED in....
b2ContactFactory.prototype.Destroy = function (contact) {...}
// then Contacts.destroyFcn callback(s) are privately REGISTERED in....
b2ContactFactory.prototype.InitializeRegisters() {...}
...via...
this.AddType = function (createFcn, destroyFcn, type1, type2) {...}
...BUT... THOSE privately registered ARE four of the un-implimented static class function from above...
b2PolygonContact.Destroy = function (contact, allocator) {}
b2EdgeAndCircleContact.Destroy = function (contact, allocator) {}
b2PolyAndCircleContact.Destroy = function (contact, allocator) {}
b2PolyAndEdgeContact.Destroy = function (contact, allocator) {}
So I havn't tested it yet but it looks like box2dweb just gives you the Destroy callback/handler functions and you have to read the source to find all the properties you need to null. [Edit] In combination with b2Contact.prototype.Reset(fixtureA, fixtureB)
But either way pretty confident the functions above(possibly incomplete) are callback/handlers, and can be used to null your way back to performance for anyone else who stumbles across this problem. Pretty sure Totti's moved on(dont forget to handle your 'this' scope in callbacks).

AS3 Display Objects & Sprites (Adding children based on a random number)

ActionScript 3.0
Essentially I need a function that displays a random number of butterfly objects. (Also I should make a reset function that resets the first function.)
I find though that the butterflies are not even displayed to begin with, It seems that the children are not being added to the stage even though I used the addChild().
Any help is appreciate thanks!
// Random Number
var randomNumber : int = Math.floor(Math.random() * 8);
// New Sprite
var bContainer: Sprite = new Sprite();
this.addChild(bContainer);
var butterfly: MovieClip = new Butterfly();
bContainer.addChild(butterfly);
//Function to Create Butterfly Objects:
function showButterfly(randomNumber:int):void {
while(bContainer.numChildren < randomNumber){
bContainer.addChild(butterfly);
}
//Reset Function, I am not sure about this (especially the second one)
function button(evt:MouseEvent): void {
if(numChildren>0) {
removeChildAt(0);
}
if(numChildren==0) {
showButterfly();
}
}
// Event Listener
button.addEventListener(MouseEvent.MOUSE_DOWN);
//
I'm not completely sure, but aren't you suppose to set the width and height for the sprite that works as a holder object?
var bContainer: Sprite = new Sprite();
bContainer.graphics.beginFill(0xffffff);
bContainer.graphics.drawRect(0, 0,stage.stageWidth, stage.stageHeight);
addChildAt(bContainer, 0);

Help on Removal of Dynamically Created sprites

import flash.display.Sprite;
import flash.net.URLLoader;
var index:int = 0;
var constY = 291;
var constW = 2;
var constH = 40;
hydrogenBtn.label = "Hydrogen";
heliumBtn.label = "Helium";
lithiumBtn.label = "Lithium";
hydrogenBtn.addEventListener (MouseEvent.CLICK, loadHydrogen);
heliumBtn.addEventListener (MouseEvent.CLICK, loadHelium);
lithiumBtn.addEventListener (MouseEvent.CLICK, loadLithium);
var myTextLoader:URLLoader = new URLLoader();
myTextLoader.addEventListener(Event.COMPLETE, onLoaded);
function loadHydrogen (event:Event):void {
myTextLoader.load(new URLRequest("hydrogen.txt"));
}
function loadHelium (event:Event):void {
myTextLoader.load(new URLRequest("helium.txt"));
}
function loadLithium (event:Event):void {
myTextLoader.load(new URLRequest("lithium.txt"));
}
var DataSet:Array = new Array();
var valueRead1:String;
var valueRead2:String;
function onLoaded(event:Event):void {
var rawData:String = event.target.data;
for(var i:int = 0; i<rawData.length; i++){
var commaIndex = rawData.search(",");
valueRead1 = rawData.substr(0,commaIndex);
rawData = rawData.substr(commaIndex+1, rawData.length+1);
DataSet.push(valueRead1);
commaIndex = rawData.search(",");
if(commaIndex == -1) {commaIndex = rawData.length+1;}
valueRead2 = rawData.substr(0,commaIndex);
rawData = rawData.substr(commaIndex+1, rawData.length+1);
DataSet.push(valueRead2);
}
generateMask_Emission(DataSet);
}
function generateMask_Emission(dataArray:Array):void{
var spriteName:String = "Mask"+index;
trace(spriteName);
this[spriteName] = new Sprite();
for (var i:int=0; i<dataArray.length; i+=2){
this[spriteName].graphics.beginFill(0x000000, dataArray[i+1]);
this[spriteName].graphics.drawRect(dataArray[i],constY,constW, constH);
this[spriteName].graphics.endFill();
}
addChild(this[spriteName]);
index++;
}
Hi, I am relatively new to flash and action script as well and I am having a problem getting the sprite to be removed after another is called. I am making emission spectrum's of 3 elements by dynamically generating the mask over a picture on the stage. Everything works perfectly fine with the code I have right now except the sprites stack on top of each other and I end up with bold lines all over my picture instead of a new set of lines each time i press a button.
I have tried using try/catch to remove the sprites and I have also rearranged the entire code from what is seen here to make 3 seperate entities (hoping I could remove them if they were seperate variables) instead of 2 functions that handle the whole process. I have tried everything to the extent of my knowledge (which is pretty minimal # this point) any suggestions?
Thanks ahead of time!
My AS3 knowledge is rather rudimentary right now but I think two things may help you.
You could use removeChild before recreating the Sprite. Alternatively, just reuse the Sprite.
Try to add this[spriteName].graphics.clear(); to reset the sprite and start redrawing.
function generateMask_Emission (dataArray : Array) : void {
var spriteName:String = "Mask"+index;
trace(spriteName);
// Don't recreate if sprite object already created
if (this[spriteName] == null)
{
this[spriteName] = new Sprite();
// Only need to add sprite to display object once
addChild(this[spriteName]);
}
for (var i:int= 0; i < dataArray.length; i+=2)
{
this[spriteName].graphics.clear();
this[spriteName].graphics.beginFill(0x000000, dataArray[i+1]);
this[spriteName].graphics.drawRect(dataArray[i],constY,constW, constH);
this[spriteName].graphics.endFill();
}
index++;
}
Just in case anyone was curious or having a similar problem. Extremely simple fix but here is what I did.
Also should mention that I don't think that the graphics.clear function actually fixed the problem (though I didn't have the sprite being cleared properly before), but I believe the problem lies in the beginning of the onloaded function where 3 of those variables used to be outside of the function.
import flash.display.Sprite;
import flash.net.URLLoader;
import flash.events.Event;
var constY = 291; //this value represets the Y value of the bottom of the background spectrum image
var constW = 2; //this value represents the width of every emission line
var constH = 40; //this value represents the height of every emission line
//Create Button Labels
hydrogenBtn.label = "Hydrogen";
heliumBtn.label = "Helium";
lithiumBtn.label = "Lithium";
//These listen for the buttons to be clicked to begin loading in the data
hydrogenBtn.addEventListener (MouseEvent.CLICK, loadHydrogen);
heliumBtn.addEventListener (MouseEvent.CLICK, loadHelium);
lithiumBtn.addEventListener (MouseEvent.CLICK, loadLithium);
var myTextLoader:URLLoader = new URLLoader();//the object to load in data from external files
myTextLoader.addEventListener(Event.COMPLETE, onLoaded);//triggers the function when the file is loaded
var Mask:Sprite = new Sprite(); //This sprite will hold the information for the spectrum to be put on stage
function loadHydrogen (event:Event):void {
myTextLoader.load(new URLRequest("hydrogen.txt"));//starts loading Hydrogen emisson data
}
function loadHelium (event:Event):void {
myTextLoader.load(new URLRequest("helium.txt"));//starts loading Helium emission data
}
function loadLithium (event:Event):void {
myTextLoader.load(new URLRequest("lithium.txt"));//starts loading Lithium emission data
}
function onLoaded(event:Event):void {//the function that handles the data from the external file
var rawData:String = event.target.data; //create a new string and load in the data from the file
var DataSet:Array = new Array();//the array to load values in to
var valueRead1:String; //subset of array elements (n)
var valueRead2:String; //subset of array elements (n+1)
for(var i:int = 0; i<rawData.length; i++){ //loop through the string and cut up the data # commas
var commaIndex = rawData.search(",");
valueRead1 = rawData.substr(0,commaIndex);
rawData = rawData.substr(commaIndex+1, rawData.length+1);
DataSet.push(valueRead1);
commaIndex = rawData.search(",");
if(commaIndex == -1) {commaIndex = rawData.length+1;}
valueRead2 = rawData.substr(0,commaIndex);
rawData = rawData.substr(commaIndex+1, rawData.length+1);
DataSet.push(valueRead2);
}
generateMask_Emission(DataSet);//call the generateMaskEmission function on new data to fill emission lines
}
//This function loops through an array, setting alternating values as locations and alphas
function generateMask_Emission(dataArray:Array):void{
Mask.graphics.clear(); //Clears the Mask sprite for the next set of values
addChild(Mask); //Adds the blank sprite in order to clear the stage of old sprites
//This loop actually draws out how the sprite should look before it is added
for (var i:int=0; i<dataArray.length; i+=2){
Mask.graphics.beginFill(0x000000, dataArray[i+1]);
Mask.graphics.drawRect(dataArray[i],constY,constW, constH);
Mask.graphics.endFill();
}
addChild(Mask);// actually adds the mask we have created to the stage
}

Resources