I'm fairly new to javascript and box2d, i was wondering if someone knows how i can call a custom function when two objects collide. I tried using some examples that uses the b2ContactListener without any succes. I've placed an object above another and let the standard Box2d physics do it's thing.
I recieve two console outputs, the first is null and the second is Ball with the following code:
var listener = new Box2D.Dynamics.b2ContactListener;
listener.BeginContact = function(contact) {
console.log(contact.GetFixtureA().GetBody().GetUserData());
console.log(contact.GetFixtureB().GetBody().GetUserData());
};.
The two objects that need to collide are a b2_dynamicbody (ball) and a b2PolygonShape. (rectangle). Using bodyDef.userData = "Ball"; in my Ball.js and bodyDef.userData = "Mouse"; in my Mouse.js i try to identify if they are hit. Instead only the ball is displayed.
Next to that i'm sure this is not the correct way for detecting collision :P I hope i've explained it well enough, could somebody steer me in the right direction?
Ok I solved it myself, apparently I had to add my custom event to the world I create with box2d. So, the issue was solved by me reading big chunks of box2d documentation/manual which can be found here:
I started with adding a String as UserData() to every object which can collide and has to do something else next to just colliding. Using the following code:
bodyDef.userData = "Car";
note: every object has to have it's own unique string.
Then I created a new contact listener (formulated in my question above) and listened for fixtures colliding with each other. When that happens, I 'save' the fixtures UserData() in variables which I can then use to look what objects collide with each other.
var contactListener = new Box2D.Dynamics.b2ContactListener;
contactListener.BeginContact = function(contact) {
var fixA = contact.GetFixtureA().GetBody().GetUserData();
var fixB = contact.GetFixtureB().GetBody().GetUserData();
// if else statement here...
};
world.SetContactListener(contactListener);
Finally, I added the last statement world.SetContactListener(contactListener); to add the event to the world, making it possible for it to listen to collisions, which I forgot to add and thus was my problem.
Hope someone finds this usefull!
Related
I am currently building a Vue application utilizing a map. The functionality is basically this. I have a position on the map and an algorythm to determine weather that position is over water or over land. If it is over water I want to reiterate over random positions nearby until my position is over land and return those coordinates. So basically my (very rudimentary for testing it out first) code looks similar to this
snippet from template
<mapbox-marker :lngLat="getPositionOverLand([0,5])" />
corresponding code
const getPositonOverLand = (coords: any, tries=100) => {
const newCoords = [coords[0], coords[1]] as [number, number];
let thisTry = 0;
while(isOverWater(newCoords) && thisTry < tries){
newCoords[0] = coords[0] + (Math.random() - 0.5)*0.5
newCoords[1] = coords[1] + (Math.random() - 0.5)*0.5
thisTry++;
}
if(thisTry === tries)
throw new Error('No position on land nearby could be found');
coords[0] = newCoords[0];
coords[1] = newCoords[1];
}
Now I debugged the algorythm and it works just fine. Usually after 5 or so attempts a position on land has been found and is returned. I also implemented a parameter for maximum tries so if a position on the open ocean e.g. is beeing put in an error is thrown after a certain number of attempts.
Now my problem in vue is this. If I change the coordinates in my function (it does not matter if I use a return statement or change the coordinates in the array) the function will be triggered again leaving me with an infinite loop.
Is there any possibility to prevent that behaviour and make sure the algorythm will only be run once or did I miss something else.
Strangly I also tried to just return the given coords just for testing and this does not cause an infinite loop.
Not sure what's triggering the reactivity. From the code posted, there is no way to tell since there are no reactive variables in sight, so it might be caused by something further upstream.
However, I see this: <mapbox-marker :lngLat="getPositionOverLand([0,5])" /> as a code smell when it comes to vue, because it has the potential to cause the type of issues you are seeing.
When you are passing a function result into as a parameter the function will re-run every time any of the parents triggers a re-render. And if you have anything in that function that triggers a re-render (which doesn't seem to be the case here, but I'm not seeing a return so I know it's not shown in entirety).
The better way to do this is to use a computed, or a data that gets updated through a watch that way the value is stored and a change is only made as needed.
Alright,
as pointed out already the problem is that every mutation on the position will trigger another reender (which still does not explain though why a direct return of the parameter itsself in the function didn't result in the same behaviour).
However I was able to solve my problem. Now I will post a little more code than included in my original question since another component with asynchronous behavior is involved. So my html looks like this
<mapbox-map :accessToken="myToken" #loaded="updateMyPosition">
<mapbox-marker :lngLat="myPosition" />
</mapbox-map>
Now the position needs to be a ref which will be updated once the map is loaded and available. This will ensure the data is only mutated once instead of the function beeing run over and over again.
I also changed the getPositionOverLand function a little bit but the functionality is basically the same as before. I just preferred the recursive variant over the while loop.
const myPosition = ref([10,10]);
const getPositionOverLand = (coords, currentTry=0, maxTries=100) => {
if(!isOverWater(coords))
return coords;
let newCoords = [
coords[0] + (Math.random() - 0.5)*0.5,
coords[1] + (Math.random() - 0.5)*0.5
];
if(currentTry >= maxTries)
throw new Error('No position on land could be found');
return (isOverWater(newCoords) && currentTry < maxTries)
? getPositonOverLand(coords, currentTry+1, maxTries)
: newCoords;
}
const updateMyPosition = map => {
myPosition.value = getPositionOverLand(myPosition.value);
}
Now while this is working I still think it is not pretty. I would prefer to use the function instead of mutation the ref because in that way working with a v-for for example would be significantly cleaner as it would be to iterate over that array in code and change each value.
For now I will make this the answer but if someone comes up with a cleaner way to do this I'd be more than happy to change the accepted answer ;)
I'm a beginner on here, so apologies in advance for naivety. I've made a simple image on Brackets using Javascript, trying to generate circles with random x and y values, and random colours. There are no issues showing when I open the browser console in Developer Tools, and when I save and refresh, it works. But I was expecting the refresh to happen on a loop through the draw function. Any clues as to where I've gone wrong?
Thanks so much
var r_x
var r_y
var r_width
var r_height
var x
var y
var z
function setup()
{
r_x = random()*500;
r_y = random()*500;
r_width = random()*200;
r_height = r_width;
x = random(1,255);
y= random(1,255);
z= random(1,255);
createCanvas(512,512);
background(255);
}
function draw()
{
ellipse(r_x, r_y, r_width, r_height);
fill(x, y, z);
}
Brackets.io is just your text editor (or IDE if you want to be technical) - so we can remove that from the equation. The next thing that baffles me is that something has to explicitly call your draw() method as well as the setup() method -
I'm thinking that you're working in some sort of library created to simplify working with the Canvas API because in the setup() method you're calling createCanvas(xcord,ycord) and that doesn't exist on it's own. If you want to rabbit hole on that task check out this medium article, it walks you thru all the requirements for creating a canvas element and then drawing on that canvas
Your also confirming that you're drawing at least 1 circle on browser refresh so i think all you need to focus on is 1)initiating your code on load and 2)a loop, and we'll just accept there is magic running in the background that will handle everything else.
At the bottom of the file you're working in add this:
// when the page loads call drawCircles(),
// i changed the name to be more descriptive and i'm passing in the number of circles i want to draw,
// the Boolean pertains to event bubbling
window.addEventListener("load", drawCircles(73), false);
In your drawCircles() method you're going to need to add the loop:
// im using a basic for loop that requires 3 things:
// initialization, condition, evaluation
// also adding a parameter that will let you determine how many circles you want to draw
function drawCircles(numCircles) {
for (let i = 0; i < numCircles; i++) {
ellipse(r_x, r_y, r_width, r_height);
fill(x, y, z);
}
}
here's a link to a codepen that i was tinkering with a while back that does a lot of the same things you are
I hope that helps - good luck on your new learning venture, it's well worth the climb!
Thank you so much for your help! What you say makes sense - I basically deleted the equivalent amount of code from a little training exercise downloaded through coursera, thinking that I could then essentially use it as an empty sandpit to play in. But there's clearly far more going on under the hood!
Thanks again!
I am creating a multiplayer game and I have an object in javascript, with a number of keys and values.
This object is called players, for holding information about each player that is connected to the game server.
name is the key of the object, and then the value of the object is a Player object which holds information such as x, y, level, etc.
Constantly I am sending a request to the server to get updated information about the players.
Because this is happening very often, I don't want the players object to be reset every time (players = {}), so instead, I am updating the object with any new information.
At the moment I am checking if name in players, and if so, I update the object like this:
players[name].x = x;
players[name].y = y;
// etc.
Otherwise, I simply create a new Player object, with the information and add it to the players object. (If a new player connected for instance)
The problem is, if a player that is already in players is no longer in the updated information from the server (i.e the player disconnected), how do I go about removing them from the object.
Is it necessary to loop trough players, and if the player is no longer in the updated information, remove it from the object, or is there any simpler way of doing this?
If there is no other way, is it a better approach to just reset the dictionary and add the data? It feels like that isn't the best way to do something simple like this.
Here is my code so far:
var newplayers = new info from server;
for(var i=0; i<newplayers.length; i++)
{
var pl = newplayers[i];
var name = pl.name;
var x = pl.x;
var y = pl.y;
// etc.
if(name in players)
{
players[name].x = x;
players[name].y = y;
// etc.
} else
newplayer = new Player();
newplayer.x = x;
newplayer.y = y;
// etc.
players[name] = newplayer;
}
}
// What if the player is no longer in the updated info, but still in players?
All help appreciated! Thanks in advance!
So you have a choice between removing outdated data from your players dictionary or rebuild it from scratch every time?
I think the answer depends a lot on how much data you have. If you have at most 20 players, it probably doesn't matter too much. If you have 1 million players it's different.
If you want to be sure, the best thing to do would be to measure it. Try both solutions with the biggest number of players you want to be able to handle and see what the impact on performance is.
Or just go with the simplest implementation and see if it's good enough for your purpose. No point in optimising before you need it.
Personally I'd just loop through players to remove the outdated data. If the performance is not good enough, then I'd optimise.
I've searched the site and some literature and couldn't get to a clear answer. I'm trying to learn unittesting while constructing a new webpage that simply works as a whiteboard to which you can add post-its.
I have a Canvas object which represents the whiteboard, and a ticket object to represent the post-its. I have (for now) global function to retrieve the one and only canvas, which i test like this:
this.testRetrieveCanvas = function()
{
var canvas = getCanvas();
this.assertTrue( canvas != null );
}
this.testCanvasType = function()
{
var canvas = getCanvas();
this.assertTrue( canvas instanceof Canvas );
}
this.testIfCanvasIsReused = function()
{
var canvas = getCanvas();
this.assertEquals( canvas, getCanvas() );
}
So, i test for three things:
Does the method return a canvas?
Is it an acutal canvas?
Does the method give me the same canvas always?
No problems so far. But a little later, i'm testing "adding the ticket to the canvas":
this.testAddTicketToCanvas = function()
{
var ticket = factory.createTicket("yellow");
var canvas = getCanvas();
canvas.addTicket( ticket );
this.assertTrue( canvas.contains( ticket ) );
};
As you can see, i'm using the getCanvas() function inside my test. Is this a dependent test now? I mean, the first three tests have to pass, if i want this test to be able to run without doubts. If it is dependent, how would i fix this?
Strictly speaking, you should override getCanvas() to return a preconstructed (i.e. the original constructor isn't called) partial mock of canvas. With that said, if canvas' constructor is an empty function and the getCanvas method has no business logic involved, you shouldn't have a problem.
I'd be more wary of the last two statements used together. canvas.addTicket( ticket ); is ok, since it's the function being tested. but then you're asserting that you have added the ticket using a method from the same object. What if this method isn't implemented yet, or returns false, or worse true? What if your addTicket method has secondary effects that could make it add the ticket to the list but change a flag that makes contains() throw an exception, or return false, or true? What if contains has certain business logic that makes it return false for the ticket your sending it, but true for the same ticket in production environment (i.e. your test ticket hasn't been properly initialized, is missing a state, has been marked as excluded from the environment's business flow), what if it has no logic now, but two months into the project the logic changes so that your test fails but all else works (a new state is added, and objects without this state are deemed non existant, except for clients a, b and c). I could go on.
My point is without the code we can't specifically answer your question, only give you pointers and general answers like the one above. If you really don't want to post code, then take into consideration all of these scenarios, and all the other scenarios you can think of, and if under these scenarios testing your code this way won't break code nor tests now and for the foreseeable future, then you're ok.
The short answer to your question is, no.
If your code does not violate any unit testing
principles as long as you adhere to these rules.
Usually, with those questions there is a huge discussion about mocking certain parts of your tests e.g. your getCanvas() function. I agree that there is reason behind this discussion and that if you want to proceed with testing or TDD in general you should dive deeper into this topic. (Please refer to this excellent article from Martin Fowler).
However, for the question if this is a valid unit test, I think it is not relevant as long as you adhere to the unit test rules.
So, I am trying to create a few circles and then I want them to fall down. Here is the code and demo: demo FireBug tells me that the method "move" is not defined for undefined objects, so something must be wrong when I want to create an array of obects. Correct my mistakes and take into consideration that I have just started learning object oriented programming in JS.
Instead of creating multiple events, create one event and loop in there:
setInterval(function(){
ctx.clearRect(0,0,1000,1000); //Clear the canvas here
for (i = 0;i<bubble.length;++i)
{
bubble[i].move();
bubble[i].redraw();
}
},32);
The problem was a scope problem.
The way you had written it i had the value of 10 in all events where they where executing.
Working fiddle: http://jsfiddle.net/some/fGChQ/16/
Your other problem is that you clear the canvas in your redraw... For every circle.
I've made a fork of this that addresses the looping issues, there also appears to have been a problem with the clearing of the canvas rectangle that was causing some interference
for (i = 0;i<bubble.length - 1; ++i)
{
console.log(bubble[i].y)
setInterval(function(){
bubble[i].move();
bubble[i].redraw();
},32);
}
bubble.length is 10, array is index 0-9. bubble[10] is undefined, so you can't call the method move on it.