Logic: Prevent infinite loop when using portals - javascript

In a game that I am currently working on, there are portals, which are linked in pairs of two. Whenever the player enters a portal, it should be teleported to the other portal that is linked to the portal that they just entered. The problem is, that once it is teleported over, the other portal see's that the player is collding with it, and will teleport it back to the first, which will send it back and so on. The code for this porject is large and over many files, but here is the most important:
(Javascript, ignore the frame and pause vaibles, those are for animation)
export class Portal extends gameObject{
constructor(x, y, size, id){
super(x, y, size);
this.currentFrame = 0;//updated when (below) > (2 below)
this.countingFrame =0;//update every time;
this.pause = 3;//frames between frames
this.id = id;
this.justVisited = false;
}
update(player, portals){
if(this.countingFrame >= this.pause){
this.currentFrame = (this.currentFrame + 1) % 4;
this.countingFrame = 0;
}else{
this.countingFrame ++;
}
if(super.checkPlayerCollision(player)){
this.justVisited = true;
for(let i in portals){
if(this.id === portals[i].id && portals[i] !== this && !portals[i].justVisited){
player.x = portals[i].x;
player.y = portals[i].y;
}
}
}else{
for(let i in portals){
if(this.id === portals[i].id && portals[i] !== this && portals[i].justVisited){
this.justVisited = false;
}
}
}
}
render(canvas, spritesheet){
super.render(canvas, spritesheet, this.currentFrame , 26);
}
}
This is one attempt at patching it up, but it still failed. How can I make it so that the player goes throught the first one as soon as it collides, comes out the second, and won't retrun to the first untill the player moves off and then back on?
EDIT:After a little bit of seaching, the answer that was most often given to this question was "Move the player outside the reach of the second portal when it teleports over there". This will not work in my case, because when the player updates, his position changes with respect to the amount of time seince the last frame. In a perfet world, that would be ok, but because it is not perfect, the player cannot reliably be on a single point. Also, it would look weird if I moved it out of the hitbox entirely, so that doesn't work either.

Related

why does javascript `for` loop rapidly mute/unmute video

I have an in-house electron app for playing videos with filters. Source code here. On most computers, the code works flawlessly, but on the booth computer where the video is shown to a class of students, when a mute filter is applied, the player.mute() and player.unmute() rapidly alternate many times per second. This only happens for some of the mute filters on some movies on some computers. I need help to track down the source of the problem.
The relevant part for this question (I think) is in script.js, lines 224-258.
for (var i = 0; i < numAnnotations; i++) {
var vMuted = player.video_obj.muted
var vBlanked = player.video_obj.classList.contains('blanked')
var vBlurred = player.video_obj.classList.contains('blurred')
var a = player.annotations[i]
var aStart = a['start']
var aEnd = a['end']
var aType = a['type']
var aDetails = a['details']
switch (a['type']) {
case 'skip':
if (time >= aStart && time < aEnd) {
console.log('skipped to '+Number(aEnd).toFixed(3))
player.skip_to(aEnd)
}
break
case 'mute':
case 'mutePlugin':
if (currently.muting === -1 || currently.muting === i) { //if no annotation is currently muting or *this* current annotaiton is muting
if (time >= aStart && time < aEnd) { //if within annotation time
if (!vMuted) {
console.log('mute on')
currently.muting = i
player.mute()
}
} else {
if (vMuted) {
console.log('mute off')
currently.muting = -1
player.unmute()
}
}
}
This code usually works flawlessly, but it sometimes fails at very inconvenient times. What is the source of the problem, or how can I debug it myself?
Turns out the problem was only with videos that were not the first one loaded after launching the player. We were able to track down that we were getting multiple eventListeners that were fighting with each other. We just had add to add a tracker to only add event listeners when they didn't already exist.

Collision detection is not returning inside limits

Full code here
I am trying to setup the functions to detect collisions and for now just log to the console. This is the section for checkCollision function;
Player.prototype.update = function(dt) {
checkCollision(this.leftLimit, this.rightLimit);
this.leftLimit = this.x - 40.5;
this.rightLimit = this.x + 40.5;
}
function checkCollision(playerl,playerr) {
for (var i = 0; i < 5; i++) {
var thisEnemy = allEnemies[i];
if (thisEnemy.leftlimit > playerl && thisEnemy.rightLimit < playerr) {console.log("1")}
else {console.log('else')}
}
}
Question
The character is never registering as colliding with the enemy, why is this not working?
Testing/Debugging
I know this function is working as consoles logging else, I've also put logging in other locations and when in the Enemy.prototype.update function, console was showing values like 202.000000093, since the for..else function is using < or >, not absolute values, that should be fine, but still nothing is matching inside the player left and right limits. I also tried changing the Enemy limits to be smaller, +/- 40.5, incase the enemy was too wide to fit inside the player limits.
player.leftLimit and player.rightLimit are undefined when checkCollision method is first running
I added a better if statement to check if there's a collision;
if (
thisEnemy.leftLimit < player.rightLimit &&
thisEnemy.rightLimit > player.leftLimit &&
thisEnemy.upperLimit > player.lowerLimit &&
thisEnemy.lowerLimit < player.upperLimit) {
console.log("collision");
}

Phaser arcade physics, use overlap to switch only once but keeps firing

I'm working on an action game using the Phaser framework, and I want the player to be able to touch certain switch tiles to turn other enemies off and back on. So it should work like this:
The player overlaps a special switch tile
Execute the switch action, only once
Ignore further overlapping until player moved off of the tile
When player moves off a switch tile, only then should it start checking for overlap again
Repeat from step 1 when player overlaps again (or another switch tile)
I'm using arcade physics and the overlap function is working. But the problem is that the overlap keeps firing over and over again, like every single frame. What would be the best way in Phaser to get the desired result?
See screenshot below of what I mean, and I've created a sandox of my code example here:
https://phaser.io/sandbox/edit/zEVOQfgA
Your code is not working because you are resetting okoverlap to 0 on each update. What you need to do is to set the okoverlap to a state and change that state at suitable time.
I've fixed your code by saving the property in game object in create.
game.flags = {};
game.flags.okoverlap = 0;
Then in update function I've checked that property and current overlap status.
function update() {
if(game.flags.okoverlap === 1 && !checkOverlap(mushroom, theswitch)) {
game.flags.okoverlap = 0;
}
game.physics.arcade.overlap(mushroom, theswitch, handleCollide, null, this);
}
function checkOverlap(spriteA, spriteB) {
var boundsA = spriteA.getBounds();
var boundsB = spriteB.getBounds();
return Phaser.Rectangle.intersects(boundsA, boundsB);
}
The logic inside handleCollide function is changed to
if (game.flags.okoverlap != 1) {
game.flags.okoverlap = 1;
doSwitch();
}
Finally replace okoverlap in render with game.flags.okoverlap to prevent ReferenceError
game.debug.text('overlap: ' + (game.flags.okoverlap == 1 ? 'YES': (game.flags.okoverlap == -1 ? 'partial': 'no')), 20, 40);
Working sample here - https://phaser.io/sandbox/edit/ikJBIznv
I've been playing around some more and found a solution, using 2 global variables. One variable frameoverlap to check if there is an overlap, and one variable doswitch to see if the switch action was already done. Then you can handle it all in the update() function.
function update() {
// assume no overlap
frameoverlap = 0;
// do arcade.overlap
game.physics.arcade.overlap(mushroom, theswitch, handleCollide, null, this);
// check flags after arcade.overlap
if (frameoverlap == 1) {
if (doswitch == 0) {
doswitch = 1; // remember the switch was done
doSwitch();
}
} else {
if (doswitch == 1) {
doswitch = 0; // stepping off the switch tile
}
}
}
See updated code here
https://phaser.io/sandbox/edit/VTenTwgh

How can I load only one model at a time in three.js?

I am working on a program that adds items to a three.js scene, positioning them at coordinates based on how many items already exist in the scene. Currently, the problem I'm running into is that when the user chooses a "Work at Height", two people are added to the scene. These are two separate function calls, but when the scene finishes, both people are at the same coordinates. This also happens when the user clicks to add multiple people and does not wait for each to load.
Is there a way I can force my loading function to wait for the first object to finish loading so that it loads only one model at a time?
This mention of the LoadingManager got me thinking, and tried I to use that by saving the number of files loaded and total as variables, then comparing them in a while loop, but, as expected, that just slowed the scripts down so much that my browser wanted me to stop them.
// adds objects to the basket with the appropriate rotation
function createObjectBasket(filePath, scale, position, name, type) {
// Load in the object and add it to the scene.
var loader = new THREE.ObjectLoader(manager);
/*while (gl_itemsLoaded < gl_itemsTotal) {
}*/
loader.load( filePath, function(object, materials){
// rotate
object.rotation.x = - ((Math.PI / 2) - (Math.PI / 18));
object.rotation.y = - (Math.PI - (Math.PI / 5));
object.rotation.z = 0; //- (Math.PI / 120);
// scale
object.scale.set(scale, scale, scale);
// translate
object.position.set(position.x, position.y, position.z);
// set name for easy access
object.name = name;
// add to scene
scene.add(object);
if (type == "basket") {
// add to object array for easy access
basketContents["basket"].push(object);
}
else if (type == "person") {
// add to object array for easy access
basketContents["people"].push(object);
}
else if (type == "tool") {
// add to object array for easy access
basketContents["tools"].push(object);
}
else if (type == "attachment") {
// add to object array for easy access
basketContents["attachments"].push(object);
}
});
}
Where I'm having the issue, this code is called (indirectly) via the following two lines:
objectAddRemove("person", "CWM_A");
objectAddRemove("person", "CWM_B");
Which then execute this function that calls createObjectBasket:
// add/remove objects to/from basket on click
function objectAddRemove(type, objectName) {
// if adding items
if (addRemove == "add") {
// determine if there is still room to add more objects
var room = true;
// can have <= 3 people total, so if there are 3, can't add more
if ((type == "person") && basketContents["people"].length >= 3) {
room = false;
return;
}
// no current restrictions on tools
/*else if ((type == "tool")) {
}*/
// can only have 1 of each attachment
else if ((type == "attachment") && (object[objectName]["contentsCount"] > 0)) {
room = false;
return;
}
if (room == true) {
// if it's a person
if (type == "person") {
// if it's a man
if (objectName.indexOf("M") >= 0) {
// add model
createObjectBasket(("models/" + objectName + ".json"), 1.5, personCoordsMan[basketContents["people"].length + 1], objectName, "person");
}
// if it's a woman
else {
// add model
createObjectBasket(("models/" + objectName + ".json"), 1.5, personCoordsWoman[basketContents["people"].length + 1], objectName, "person");
}
}
// if it's a tool
else if (type == "tool") {
/*createObjectBasket(("models/" + objectName + ".json"), 1.5, toolCoords[basketContents["tools"].length + 1], objectName, "tool");*/
createObjectBasket(("models/" + objectName + ".json"), 1.5, toolCoords, objectName, "tool");
}
// if it's an attachment
else if (type == "attachment") {
createObjectBasket(("models/" + objectName + ".json"), .04, attachmentCoords[objectName], objectName, "attachment");
}
// increase count
object[objectName]["contentsCount"] += 1;
console.log(objectName);
$('#' + objectName).children('.status').children('.checkMark').show();
}
}
// if removing items
else if (addRemove == "remove") {
// remove objects from arrays
if (type == "person") {
removeObjectArray("people", objectName);
// if person is found (and removed), rearrange all people to accommodate
if (itemFound == true) {
for (i = 0; i < basketContents["people"].length; ++i) {
if (basketContents["people"][i].name.indexOf("M") >= 0) {
basketContents["people"][i].position.set(personCoordsMan[i+1].x, personCoordsMan[i+1].y, personCoordsMan[i+1].z);
}
else {
basketContents["people"][i].position.set(personCoordsWoman[i+1].x, personCoordsWoman[i+1].y, personCoordsWoman[i+1].z);
}
}
}
}
else if (type == "tool") {
removeObjectArray("tools", objectName);
// if tool is found (and removed), rearrange all tools to accommodate
/*if (itemFound == true) {
}*/
}
else if (type == "attachment") {
removeObjectArray("attachments", objectName);
}
// if all objects of that id have been removed, hide remove x mark
if (object[objectName]["contentsCount"] <= 0) {
$('#' + objectName).children('.status').children('.xMark').hide();
}
// if, after removing, person/object count is now 0, no remaining items can be removed, so switch to add
if ((steps[currentStep] == "people") && (basketContents["people"].length <= 0)) {
addItems();
}
else if ((steps[currentStep] == "objects") && ((basketContents["tools"].length + basketContents["attachments"].length) <= 0)) {
addItems();
}
}
// if no remaining items can be removed on this page
else {
addItems();
}
}
objectAddRemove is also called whenever a person clicks on an image representing the desired object, so I need a way to wait for models from previous clicks to load, also (in addition to models loaded automatically through the code).
To test/view further code, you can visit this link. Select a "Work at Height", weight unit, then skip to "Add Operators". It will show that two people are in the basket (checked) but only one is visible in the basket. If you click "Remove Items" then the visible person to remove the visible person, the hidden one will show.
Thank you so much!!!
Loading external data almost always happens asynchronously and therefore making sure one model is loaded after another requires handling the onLoad events.
I'm not sure if LoadingManager supports loading models one after another.
But you can implement a simple loading queue yourself. Something in the line of:
var myQueue = ['model1.dae', 'model2.dae'];
function loadFromQueue()
{
if (myQueue.length == 0) return;
// Takes the first name from array and remove it from the array
var name = myQueue.shift();
// Call the loader and provide an onLoad event handler
loader.load(name, function(model){
// Do what you need to do with the model,
// usually it's scene.add(model) and some transformations.
// Call the next item in the queue
loadFromQueue();
});
}
Now this is a very crude queue and there are better ways to do it. But I'm using it the simplest demonstration how you can use the onLoad event handler to load models one after another.
I presume the next hurdle you'll hit will be how to pass some values to the event handler. Come back again then!
Thanks to the suggestion from #Matey, I ended up just using timeouts to prevent the objects from displaying on top of one another. I was hoping to avoid that, since the amount of time it takes can vary by model and environment, but that was the only thing that worked for the clicks. The load queue was also still loading the models on top of one another, and I decided since I was already risking the timeout, it would be easier to implement that than to fix it.
I added timeouts to my click event using the accepted answer here, and then just added a timeout to the second person I was adding:
objectAddRemove("person", "CWM_A");
// add second person on timeout so people don't appear on top of each other
setTimeout(function() {
objectAddRemove("person", "CWM_B");
},
That was the only place that automatically added objects needed some delay, so hopefully that does it. I'm just a little concerned with how it will function across different machines.
I actually ended up getting around this by taking a different approach - I needed variables to keep track of the objects, so I checked whether objects had been loaded based on the variable values (which were set quickly) rather than the number of three.js objects (which took much longer to load). This worked much, much better and is more reliable across systems than using a timeout.

life decrease on collision

I am making a little game using HTML5 Canvas and javascript. I am so far that I have a kite moving some sort of power up on collision and an obstacle on collision.
Now I'm at the point I want to add lives and when you hit an obstacle your life will decrease 1.
I tried some stuff and when you hit an obstacle the life decreases but it decreases constantly and the player image gets removed instead of the obstacle image.
here is the life thing you can check all the code there.
http://nickzijlstra.com/kite
Here the code I think is the most important for the problem.
function hitObject(player, obj){
var a = (obj.x - player.x),
b = (obj.y - player.y),
c = Math.sqrt(a*a + b*b),
r0 = player.image.width/2,
r1 = obj.image.width/2;
if (c < r0+r1) {
player.drawable = false;
lifes -=1;
window.location.reload(true);
}
}
If someone sees the problem or knows the solution I would really appreciate it!
The reason the player disappears is because of this line in the hitObject function:
player.drawable = false;
This will cause the player to not be drawn because of this condition in your drawing function:
if (player.drawable == true) {
context.drawImage(player.image, player.x, player.y, player.image.width, player.image.height);
}
I presume you actually want to move the obj back to a random spot on the top of the screen if the player gets hit. It doesn't do this at the moment, which is why the lives go down rapidly: the object hits the player, it removes a life, and then the next frame it hits the player again (even though the player isn't visible).
What you might want is something like:
...
if (c < r0+r1) {
lifes -=1;
// Respawn the object.
obj.y = -50;
obj.x = Math.random() * canvas.width;
...
At a guess, I'd say that you should replace the
player.drawable = false;
with
obj.drawable = false;
and wrap the whole collision detection inside of an if obj.drawable=true so that removed obstacles won't collide with the kite.

Categories