Make a object rotate back to origin in three js - javascript
I am a beginner with THREE.js and currently have a animation that spins a 3D model around its Y axis but for the website I am creating I need to rotate it back to origin during the timespan of 90 frames. I have tried using a various different code snippets but the problem is that it seems to work half the time and the other half it totally messes it up. I believe that it messes up more if I let it spin for a while.
Here is my current code for the rotation:
//angle
angle = ((room.rotation.y * 180) / Math.PI) % 360;
total_deg = room.rotation.y;
//for calculating the lap:
if(angle > 0) {
positiv = true;
} else {
positiv = false;
}
if(angle < last_angle) {
direction = "down";
} else {
direction = "up";
}
if(direction == "up" && positiv == true) {
lap = 1;
} else if(direction == "down" && positiv == true) {
lap = 2;
} else if(direction == "down" && positiv == false) {
lap = 3;
} else if(direction == "up" && positiv == false) {
lap = 4;
}
if(last_lap == 4 && lap == 1) {
room.rotation.y = 0;
}
last_angle = ((room.rotation.y * 180) / Math.PI) % 360;
//rotating it back depending on what lap it currently is on
if(lap == 4) {
room.rotation.y += -total_deg / 90;
}
if(lap == 3) {
room.rotation.y += -(Math.PI - Math.abs(total_deg)) / 90;
}
if(lap == 2) {
room.rotation.y += (Math.PI - Math.abs(total_deg)) / 90;
} else {
room.rotation.y -= total_deg / 90;
}
//reset the rotation when the room has spun a lap - don't know why this is works but seems to fix some of the rotation problems
if(last_lap == 4 && lap == 1) {
room.rotation.y = 0;
}
Have been stuck with this problem for days so any help would be appreciated
I hope I'm understanding your objective properly but to me it seems that there may be some confusion with degrees and radians as the units. Three.js uses radians, but I'm seeing 360 in your code which I'm unsure about.
Anyway this code seems like what you're trying to do, notice how it divides 2 pi (one rotation in radians) into 90 frames.
var i=0;
function update( event ) {
//angle
if(i++<90){
this.rotation.y += 2*Math.PI/90;
}
}
I figured it out using a Quaternion of the objects first rotation and rotating to that later. This example was very helpful: https://threejs.org/examples/#webgl_math_orientation_transform
Related
JavaScript collision detection with objects in a multi-dimensional array
I'm currently coding a Pac-man clone with p5.js, and have ran into an issue. I have created a function which draws the map by using a multi-dimensional array, drawing a wall block where a 1 is, and nothing where a 0 is. This works fine, however i'm struggling to detect collision between the player and the walls. I have tried to use a for loop to go through the array, checking the x and y co-ordinates to see if there is a collision, however it doesn't register at all.This is the code i have used to detect collision: for(i=0;i<walls.length;i++){ walls[i].draw(); if(player.x > walls[i].x && player.x < walls[i].x + gridsize && player.y > walls[i].y && player.y < walls[i].y + gridsize){ console.log('collision') } } I can't see where the issue is here, as it seems to have worked in other programs. This runs in the Draw() function, meaning it loops 30 times a second. This is the full code, incase the issue lies elsewhere: var gridsize = 20; var walls = []; var dots = []; var player; var score =0; var maps = [[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,1,1,0,1,1,1,1,1,1,0,1,1,0,1], [1,0,1,1,0,0,0,0,0,0,0,0,1,1,0,1], [1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,1], [1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1], [1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1], [1,0,1,0,0,1,0,0,0,0,1,0,0,1,0,1], [1,0,1,0,0,1,0,0,0,0,1,0,0,1,0,1], [1,0,1,0,0,1,1,1,1,1,1,0,0,1,0,1], [1,0,1,0,0,0,0,2,0,0,0,0,0,1,0,1], [1,0,1,1,1,0,1,1,1,1,0,1,1,1,0,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]]; function setup(){ createCanvas(320,320); frameRate(30); createMap(); } function draw(){ background(51); for(i=0;i<walls.length;i++){ walls[i].draw(); if(player.x > walls[i].x && player.x < walls[i].x + gridsize && player.y > walls[i].y && player.y < walls[i].y + gridsize){ console.log('collision') } } fill('white'); text('Score: ' + score, 5,10); for(i=0;i<dots.length;i++){ if(player.x == dots[i].x && player.y == dots[i].y && dots[i].collect == false){ dots[i].collect = true; score++; } dots[i].draw(); } player.update(); player.draw(); } function Block(x,y){ this.x = x; this.y = y; this.draw = function(){ fill('black'); rect(this.x,this.y,gridsize,gridsize); } } function Dot(x,y){ this.x = x; this.y = y; this.collect = false; this.draw = function(){ if(!this.collect){ fill('yellow'); ellipse(this.x+gridsize/2,this.y+gridsize/2,gridsize/3,gridsize/3); }else if(this.collect){ noFill(); noStroke(); ellipse(this.x+gridsize/2,this.y+gridsize/2,gridsize/3,gridsize/3); } } } function Player(x,y){ this.x = x; this.y = y; this.update = function(){ if(keyIsDown(UP_ARROW) && frameCount%5 == 0){ player.y -= gridsize; } if(keyIsDown(DOWN_ARROW) && frameCount%5 == 0){ player.y += gridsize; } if(keyIsDown(LEFT_ARROW) && frameCount%5 == 0){ player.x -= gridsize; } if(keyIsDown(RIGHT_ARROW) && frameCount%5 == 0){ player.x += gridsize; } } this.draw = function(){ fill('blue'); ellipse(this.x+gridsize/2,this.y+gridsize/2,gridsize/1.2,gridsize/1.2); } } function createMap(){ for(i=0;i<maps.length;i++){ for(j=0;j<maps[i].length;j++){ if (maps[i][j] == 1){ walls.push(new Block(j*gridsize,i*gridsize)); }else if(maps[i][j] == 0){ dots.push(new Dot(j*gridsize,i*gridsize)) }else if(maps[i][j] = 2){ player = new Player(j*gridsize,i*gridsize) } } } } I presume the issue lies with the fact that the walls are stored in an array, however i have done very similar programs in which the same code works.
PacMan controls The best way to check for this type of map is to use the player's input. The player must line up with the walls so assuming the player position is relative to the top left and the player is one map unit wide and deep. Key input requests a direction to move dx, dy hold the directions which could be more than one at a time. If dx or dy are not 0 then first check if the player is lined up with a passage, if so then check if a block is in the direction of travel. If the player is not lined up or blocked set the movement var to 0 After checking both x and y directions, then if dx or dy have a value then that must be a valid move. Code changes Remove the player collision checking code from the main loop and call the player update function with the current map as the 2D original. player.update(maps); // move the player Change the Player and update function function Player(x,y){ this.x = x; this.y = y; var dx = 0; // hold current movement var dy = 0; const speed = 1; // per Frame pixel speed best as an integer (whole number) and evenly divisible into gridSize // need the map so that must be passed to the update function this.update = function(map){ // assuming keys are held to move up to stop dx = 0; // stop by default dy = 0; if (keyIsDown(UP_ARROW)) { dy = -speed } if (keyIsDown(DOWN_ARROW)) { dy = speed } if (keyIsDown(LEFT_ARROW)) { dx = -speed } if (keyIsDown(RIGHT_ARROW)){ dx = speed } // get player map coords var x = Math.floor(this.x / gridSize); // get map coord var y = Math.floor(this.y / gridSize); // get map coord // the two if statement are best aas function // so you can select which one to call first. Depending on the latest // new keys down and if the result allows movement in that // direction then set the other direction to zero. if (dy !== 0) { // is moving up or down? if (this.y % gridsize === 0) { // only if lined up if (dy > 0){ // is moving down if (map[y + 1][x] === 1) { // down blocked dy = 0; } }else if (map[y - 1][x] === 1) { // up blocked dy = 0; } } else { // block move if not lined up with passage dy = 0; } } if(dx !== 0){ // is moving left or right? if (this.x % gridsize === 0) { // only if lined up if (dx > 0) { // is moving right if (map[y][x + 1] === 1) { // right blocked dx = 0; } } else if (map[y][x - 1] === 1) { // left blocked dx = 0; } } else { // block move if not lined up with passage dx = 0; } } // could have two moves, but can only move left right or up down // you need to add some input smarts to pick which one // this just favours up down if(dy !== 0) { dx = 0 }; // only valid moves will come through the filter above. // so move the player. this.x += dx; this.y += dy; } Adding more smarts Note I have changed the way the player moves, I set a speed per frame (1 pixel) that must be an even divisor of gridSize. The code above is the simplest implementation. This type of games needs some extra smarts in controls. You should check in the direction of the newest key down. Ie if the player traveling down and right is pressed then moving right should have priority. If player moving right and left is pressed then you should move left, not keep moving right. Extras While looking at this question I wanted to visualize the map. Maps as arrays are painful to create and modify, and very hard to find mistakes in. Much easier as a a set of strings that gets converted to an array at run time. As i have done the conversion no point wasting it. maps is identical to the original array but now easier to read and change. const maps = [ "################", "# #", "# ## ###### ## #", "# ## ## #", "# ###### #", "#### ####", "# ## ## #", "# # # # # #", "# # # # # #", "# # ###### # #", "# # 2 # #", "# ### #### ### #", "# #", "# ######## #", "# #", "################" ].map(row => row.split("").map(c => c === "#" ? 1 : c === " " ? 0 : 2));
I'm not quite sure why you're using rectangle-rectangle collision detection when you could just use grid-based collision detection. You could just use the array directly. But since you are using rectangle-rectangle collision, this line looks a little bit off: if(player.x > walls[i].x && player.x < walls[i].x + gridsize && player.y > walls[i].y && player.y < walls[i].y + gridsize){ You're checking whether the left edge of the player is inside the wall and whether the top edge of the player is inside the wall. But you aren't detecting the other edges. Usually you'd want to do something like this: if(rectOneRight > rectTwoLeft && rectOneLeft < rectTwoRight && rectOneBottom > rectTwoTop && rectOneTop < rectTwoBottom){ Notice how this if statement checks all of the edges, not just the top and left. But like I said, you might be better off just using grid collision detection, since you already have a grid of walls. Shameless self-promotion: here is a tutorial on collision detection. It's written for Processing, but everything should translate pretty directly to P5.js.
if the player is not sprite here then point-in-rect collision detection will be appropriate here. // point in rect collision detection function pointInRect (x, y, rect) { return inRange(x, rect.x, rect.x + gridsize) && inRange(y, rect.y, rect.y + gridsize); } // check a value is in range or not function inRange (value, min, max) { return value >= Math.min(min, max) && value <= Math.max(min, max); } // checking player is hitting the wall or not if(pointInRect(player.x,player.y,walls[i].x,walls[i].y)) { console.log('collision') }
Google Chrome freezes when my script is running
Context : I'm making a version of the popular game, Snake. And I encounter some issues when I try to avoid the appearance of the food at the same place of the parts of my snake. So I produced this code with the framework Phaser : generateFood: function() { var randomX, randomY; var rightLocation = false; var foodOnSnake = false; while(rightLocation === false) { randomX = Math.floor(Math.random() * (this.game.width / squareSize)) * squareSize; randomY = Math.floor(Math.random() * (this.game.height / squareSize)) * squareSize; foodOnSnake = false; for (var i = 0; i < snake.length; i++) { if (snake[i].x === food.x && snake[i].y === food.y) { foodOnSnake = true; break; } } if(foodOnSnake === false) { rightLocation = true; } } food = this.game.add.sprite(randomX, randomY, 'snake', 15); } The aim, is to create some random coordinates on the game. And, while the food is generate on a part of the snake (the for loop), I will generate other coordinates. But for unknown reason, after my snake is eaten the first food, the game crashed and the tab of Google Chrome does not responding. I think there is a mistake with a loop but I can't find it.
You compare the coordinates of the snake's segments with food.x and food.y, which you never update inside the loop: if (snake[i].x === food.x && snake[i].y === food.y) { I believe you want to compare it to randomX and randomY instead: if (snake[i].x === randomX && snake[i].y === randomY) { Depending on the value of food's coordinates, your function likely results in an infinite loop.
How do I speed up my snake?
So this part of my js is moving the snake. if(direction === "right"){ newX ++; } else if(direction === "left"){ newX --; } else if(direction === "up"){ newY --; } else if(direction === "down"){ newY ++; When I tried incrementing it by 2 when score is more than 10, it breaks its body. Here's the code pen link
As #MikeC suggests, just modify your speed. You can define this function: function runGame(newSpeed) { if(typeof loop_game !== "undefined") clearInterval(loop_game); loop_game = setInterval(render, newSpeed); } Then at the start of the game, you call this function: function startGame(){ . . . runGame(speed); } And later on, when you need to increase the speed: if (speed>10) { speed -= 10; runGame(speed); } http://codepen.io/anon/pen/xZJRdQ I modified your codepen to make it work and scored 18 points :) can u beat me?
Handle the cases when newX overflows past your boundary (which I assume is 10): newX = (newX + 2 < 10) ? newX + 2 : 10; This will check whether adding 2 to newX will make it more than 10. If it doesn't, then it will add 2. If it does, then it will simply set newX to 10.
Separating Rects Javascript
Essentially, what I'm doing is placing a bunch of random width/height rects onto a grid (near the center of it), then pushing them all away from each other until none of them overlap. I have another version where I check for collisions before I place them on the grid, but that's not what I'm going for in this build. I'm wondering if someone can explain a better way to go about this? What I've tried so far is something similar to: let r1/r2 = rect1/rect2 do { var ox = Math.max(0, Math.min(r1.x + r1.w, r2.x + r2.w) - Math.max(r1.x, r2.x)), oy = Math.max(0, Math.min(r1.y + r1.h, r2.y + r2.h) - Math.max(r1.y, r2.y)), dx = r2.x - r1.x, dy = r2.y - r1.y; if (ox > 0 && oy > 0) { if (ox >= oy) { if (r1.x >= r2.x && Math.random() > .1) { r1.x += ox; spaced = true; continue; } else { r1.x -= ox; spaced = true; continue; } } else { if (r1.y >= r2.y && Math.random() > .1) { r1.y += oy; spaced = true; continue; } else { r1.y -= oy; spaced = true; continue; } } } } while ( /* stuff */ ) the random is only there because I will run into times when a certain rect gets pushed back and forth and never gets free and causes an infinite loop. This way is horribly inefficient however.
I believe what your trying to accomplish is known as a packing problem http://en.wikipedia.org/wiki/Packing_problem. If you just search stack overflow for "2d bin packing" you should be able to find all you need to roll a much more efficient algorithm.
Internal loop only runs once, containing loop runs endlessly
noob question I'm afraid. I have a loop that runs and rotates the hand of a clock and an internal loop that checks the angle of the hand if it is 90, 180, 270 and 360. On these 4 angles the corresponding div is displayed and its siblings removed. The hand loops and loops eternally, which is what I want, but the angle check only runs the loop once through the whole 360. As the hand passes through the angles it is correctly displaying and removing divs but is doesn't continue after the first revolution of the clock. I've obviously messed up somewhere and there is bound to be a more efficient way of doing all this. I am using jQueryRotate.js for my rotations. Thanks for your time. jQuery(document).ready(function() { var angle = 0; setInterval(function() { jQuery("#hand").rotate(angle); function movehand() { if (angle == 90) { jQuery("#intervention").fadeIn().css("display","block").siblings().css("display","none"); } else if (angle == 180) { jQuery("#management").fadeIn().css("display","block").siblings().css("display","none"); } else if (angle == 270) { jQuery("#prevention").fadeIn().css("display","block").siblings().css("display","none"); } else if (angle == 360) { jQuery("#reaction").fadeIn().css("display","block").siblings().css("display","none"); } else { movehand; } }; movehand(); angle+=1; },10); });
Once angle reaches 360 do you not need to reset it to 0? http://jsfiddle.net/DHxBm/2/
jQuery(document).ready(function(){ var angle = 0; setInterval(function(){ jQuery("#hand").rotate(angle); function movehand(){ if (angle == 90) { jQuery("#intervention").fadeIn().css("display","block").siblings().css("display","none"); } else if (angle == 180) { jQuery("#management").fadeIn().css("display","block").siblings().css("display","none"); } else if (angle == 270) { jQuery("#prevention").fadeIn().css("display","block").siblings().css("display","none"); } else if (angle == 360) { jQuery("#reaction").fadeIn().css("display","block").siblings().css("display","none"); angle=0; } else {movehand;} }; angle+=1; },10); });