How are trigonometric functions used in this sketch to make one object move towards another? - trigonometry

So I asked a someone to help me with a small project ( ball chases ball you control one)
and he gave me a trigonometric thing, it works very well can someone explain trigonometry to me?
Heres the code he gave me:
var aiX = 0;
var aiY = 0;
var playerX = 200;
var playerY = 200;
draw = function () {
background(255);
playerX = mouseX;
playerY = mouseY;
fill(0, 255, 0);
ellipse(playerX, playerY, 50, 50);
fill(255, 0, 0);
ellipse(aiX, aiY, 50, 50);
var angle = atan2(playerY - aiY, playerX - aiX);
aiX += cos(angle);
aiY += sin(angle);
if (dist(playerX, playerY, aiX, aiY) < 50) {
text("ouch!", 100, 100);
}
};
How i changed it:
var aiX = 200;
var aiY = 0;
function setup() {
playerX = 200;
playerY = 200;
}
draw = function () {
createCanvas(400, 400);
background(255);
let h = hour();
//scare anyone
if (h === 3) {
console.log("I SEE YOU");
let img = createImg(imageData, "YOU CANT HIDE EVEN IF I WONT LOAD");
}
if (isKeyPressed && keyCode === UP_ARROW) {
playerY = playerY - 5;
}
if (isKeyPressed && keyCode === DOWN_ARROW) {
playerY = playerY + 5;
}
if (isKeyPressed && keyCode === RIGHT_ARROW) {
playerX = playerX + 5;
}
if (isKeyPressed && keyCode === LEFT_ARROW) {
playerX = playerX - 5;
}
fill(0, 255, 0);
ellipse(playerX, playerY, 50, 50);
fill(255, 0, 0);
ellipse(aiX, aiY, 50, 50);
function reset() {
playerX = 200;
playerY = 200;
aiX = 200;
aiY = 15;
}
var angle = atan2(playerY - aiY, playerX - aiX);
aiX += cos(angle);
aiY += sin(angle);
if (dist(playerX, playerY, aiX, aiY) < 50) {
console.log("GAMEOVER");
reset();
}
};
const imageData =
"data:image/jpeg;base64,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";
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
I just started learning p5js about a week ago, so sorry if my code is bad or sloppy...
Editor:https://editor.p5js.org/

Ironically the use of trigonometry in this example is actually unnecessary. The objective is to move a circle positioned at aiX, aiY closer to a second circle positioned at payerX, playerY. It is trivial to compute a vector from the ai position to the player position by taking the difference between the two components in each direction: playerX - aiX is the offset from ai to player in the horizontal direction, and playerY - aiY is the offset from ai to player in the vertical direction. Obviously you won't want to change the position of the ai circle by the entire offset, since that would be instant game over. If you want to fix the speed of movement at a particular level, you simply need to divide each component by the length of the vector, and then if you want a speed other than 1, multiply by the desired speed. Here is the code that does this without any trigonometry:
let aiX = 200;
let aiY = 0;
let playerX = 200;
let playerY = 200;
function setup() {
createCanvas(400, 400);
}
function draw() {
background(255);
if (isKeyPressed && keyCode === UP_ARROW) {
playerY = playerY - 5;
}
if (isKeyPressed && keyCode === DOWN_ARROW) {
playerY = playerY + 5;
}
if (isKeyPressed && keyCode === RIGHT_ARROW) {
playerX = playerX + 5;
}
if (isKeyPressed && keyCode === LEFT_ARROW) {
playerX = playerX - 5;
}
fill(0, 255, 0);
ellipse(playerX, playerY, 50, 50);
fill(255, 0, 0);
ellipse(aiX, aiY, 50, 50);
let offsetX = playerX - aiX;
let offsetY = playerY - aiY;
// pythagorean theorem: given a right triangle, the length of the
// hypotenuse is the square root of side A squared plus side B squared
let length = sqrt(offsetX * offsetX + offsetY * offsetY);
offsetX = offsetX / length;
offsetY = offsetY / length;
// The total distance moved when incrementing will now be 1
aiX += offsetX;
aiY += offsetY;
if (dist(playerX, playerY, aiX, aiY) < 50) {
console.log("GAMEOVER");
reset();
}
}
function reset() {
playerX = 200;
playerY = 200;
aiX = 200;
aiY = 15;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
However, since you asked about the trigonometry, here's how the original example works:
The atan2 function, which implements the inverse tangent function (a.k.a arctangent), takes the vertical offset between two points and the horizontal offset between the two points, and returns the angle of the line between the two points relative to some reference direction (the positive X direction). This is the inverse of the tangent function which takes an angle, and returns the ratio of the opposite to the adjacent sides of a right triangle with that angle. The reason for using atan2 which takes the two offsets separately vs. the atan function which takes the ratio as a single value, is that atan will not return correct angles when the x offset is negative.
Now, once you have the angle of a line from the ai position to the player position, you can use other trigonometric functions to convert back to offsets: namely sine and cosine (i.e. sin and cos). The sine function takes an angle and returns the ratio of the opposite side of a right triangle with that angle and its hypotenuse. The cosine function takes an angle and returns the ratio of the adjacent side of a right triangle with that angle and its hypotenuse. So if you use sin(angle) as the vertical offset to move by, and cos(angle) as the horizontal offset, then you are moving in the direction of angle by an offset of 1.
To better understand all of this I recommend you watch the first few chapters of The Nature of Code by Daniel Shiffman.
I also think this p5.js sketch by spencer eastcott is helpful for visualizing these concepts.

Related

Phaser Scrollable Text Box Tutorial not working on mobile

I recreated the scrolling text box tutorial in my game. However, it is running a bit glitchy on mobile. For example, if I swipe up, the text first goes down for a second and then follows my finger up. You’ll see the problem if you open the tutorial on mobile. Any thoughts? I copied my code below.
var graphics = scene.make.graphics();
graphics.fillRect(x, y + 10, width, height - 20);
var mask = new Phaser.Display.Masks.GeometryMask(scene, graphics);
var text = scene.add.text(x + 20, y + 20, content, {
fontFamily: 'Assistant',
fontSize: '28px',
color: '#000000',
wordWrap: { width: width - 20 }
}).setOrigin(0);
text.setMask(mask);
var minY = height - text.height - 20;
if (text.height <= height - 20) {
minY = y + 20;
}
// The rectangle they can 'drag' within
var zone = scene.add.zone(x, y - 3, width, height + 6).setOrigin(0).setInteractive({useHandCursor: true});
zone.on('pointermove', function (pointer) {
if (pointer.isDown) {
text.y += (pointer.velocity.y / 10);
text.y = Phaser.Math.Clamp(text.y, minY, y + 20);
}
});
I had the same issue. My solution is using "pointer.y" instead of "pointer.velocity.y".
Here is my code:
var previousPointerPositionY;
var currentPointerPositionY;
zone.on('pointerdown', function (pointer) {
previousPointerPositionY = pointer.y;
});
zone.on('pointermove', function (pointer) {
if (pointer.isDown) {
currentPointerPositionY = pointer.y;
if(currentPointerPositionY > previousPointerPositionY){
text.y += 5;
} else if(currentPointerPositionY < previousPointerPositionY){
text.y -= 5;
}
previousPointerPositionY = currentPointerPositionY;
text.y = Phaser.Math.Clamp(text.y, -360, 150);
}
});

why is that gl.clear(gl.COLOR_BUFFER_BIT) and requestAnimationFrame will clear all the primitives I drew before

Hi guys I been leanring WebGL and trying to make a Tetris game out of it.
I have a couple of questions I'd like to ask:
For this game I wanted to first draw the grid as the background. However I noticed that after I drew the line, if I use gl.clear(gl.COLOR_BUFFER_BIT ); after, it will clear all the lines I drew before. I understand that gl.clear(gl.COLOR_BUFFER_BIT ); is about clearing the color buffer (and you probably will ask why I would want to do that. Just bear with me. ). Then I tried use gl.uniform4f( uColor, 0, 0, 0, 1); to send the color again to the fragment shader but it doesn't help.
The snippet is like this
window.onload = function(){
getGLContext();
initShaders();
drawLines( 0, 0, 400,400 );
gl.clear(gl.COLOR_BUFFER_BIT );
gl.uniform4f( uColor, 0, 0, 0, 1);
}
For the game I need the grid as background and I need requestAnimationFrame for the game loop and will render Tetrominos inside the loop. Therefore after drawing the line I used this draw() to draw other Tetrominos. However it removes the line I drew before. And when I comment out gl.clear(gl.COLOR_BUFFER_BIT ); inside draw(), it will remove the line along with background color.
function draw() {
gl.clear(gl.COLOR_BUFFER_BIT );
gl.drawArrays(gl.TRIANGLES, 0, index*6);
requestAnimationFrame(draw);
}
Here is the demo: https://codepen.io/zhenghaohe/pen/LqxpjB
Hope you could answer these two questions. Thanks!
This is generally the way WebGL works.
WebGL is just draws into a rectangle of pixels. There is no memory of primitives. There is no structure. There is just code and the resulting canvas which is an rectangle of pixels.
Most WebGL programs/pages clear the entire canvas every frame and redraw 100% of the things they want to show every time they draw. For tetris the general code might be something like
function render() {
clear the canvas
draw the grid
draw all the stable pieces
draw the current piece
draw the next piece
draw the effects
draw the score
}
Any knowledge of primitives or other structure is entirely up to your code.
If you want the grid lines to be static then either set a static background with CSS or use another canvas
Using a background:
const gl = document.querySelector('#c').getContext('webgl');
function render(time) {
time *= 0.001;
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
drawBlocks(gl, time);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
// --- below this line not important to the answer
function drawBlocks(gl, time) {
gl.enable(gl.SCISSOR_TEST);
const numBlocks = 5;
for (let i = 0; i < numBlocks; ++i) {
const u = i / numBlocks;
gl.clearColor(i / 5, i / 2 % 1, i / 3 % 1, 1);
const x = 150 + Math.sin(time + u * Math.PI * 2) * 130;
const y = 75 + Math.cos(time + u * Math.PI * 2) * 55;
gl.scissor(x, y, 20, 20);
gl.clear(gl.COLOR_BUFFER_BIT);
}
gl.disable(gl.SCISSOR_TEST);
}
#c {
background-image: url(https://i.imgur.com/ZCfccZh.png);
}
<canvas id="c"></canvas>
Using 2 canvases
// this is the context for the back canvas. It could also be webgl
// using a 2D context just to make the sample simpler
const ctx = document.querySelector('#back').getContext('2d');
drawGrid(ctx);
// this is the context for the front canvas
const gl = document.querySelector('#front').getContext('webgl');
function render(time) {
time *= 0.001;
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
drawBlocks(gl, time);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
// --- below this line not important to the answer
function drawBlocks(gl, time) {
gl.enable(gl.SCISSOR_TEST);
const numBlocks = 5;
for (let i = 0; i < numBlocks; ++i) {
const u = i / numBlocks;
gl.clearColor(i / 5, i / 2 % 1, i / 3 % 1, 1);
const x = 150 + Math.sin(time + u * Math.PI * 2) * 130;
const y = 75 + Math.cos(time + u * Math.PI * 2) * 55;
gl.scissor(x, y, 20, 20);
gl.clear(gl.COLOR_BUFFER_BIT);
}
gl.disable(gl.SCISSOR_TEST);
}
function drawGrid(ctx) {
// draw grid
ctx.translate(-10.5, -5.5);
ctx.beginPath();
for (let i = 0; i < 330; i += 20) {
ctx.moveTo(0, i);
ctx.lineTo(330, i);
ctx.moveTo(i, 0);
ctx.lineTo(i, 300);
}
ctx.strokeStyle = "blue";
ctx.stroke();
}
#container {
position: relative; /* required so we can position child elements */
}
#front {
position: absolute;
left: 0;
top: 0;
}
<div id="container">
<canvas id="back"></canvas>
<canvas id="front"></canvas>
</div>
As for why it clears even if you didn't call clear that's because that's whqt the spec says it's supposed to do
See: Why WebGL 'clear' draw to front buffer?

Frame rate drops / efficiency problem in three.js [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 4 years ago.
Improve this question
Upon running there is a slow but consistent drop in fps. I have tried to identify the responsible function and it seems to be that:
updatepoints() and rotateTriangle() seem to be the main culprits but it's clear I have misunderstood something or used an inefficient means of calculating something somewhere
Upon further inspection using browser tools it seems to be an array and an object that are filling up the memory which I'm guessing is what is causing the frame drops.
I have also noticed that the buffer in the performance tab for the browser tools is filling up
I know bufferGeometry is the more efficient means of creating objects but I'd still like to know the cause the performance issues
Sorry to just dump code but I feel as though it'll be something obvious.
Any advice or ways of going about finding the problem and solution would be greatly appreciated
//every scene needs these
var scene, camera, renderer, controls;
//links div with canvas
var canvas = document.getElementById('canvas');
// What I need are number of particles and the length the curve goes to be uncoupled
// Each part of degree array serves one particles
// If I added a factor so:
// factor * coord *
//creating particles
var particleCount = 360;
var particles = [];
var particles2 = [];
var particles3 = [];
var SPEED = 0.01;
var radians, y, x;
var centerX = 0;
var centerY = 0;
var radius = 231.84;
var pointPositions=[];
var vupdateXvertices, updateYvertices, updateXvertices2, updateYvertices2,
updateXvertices3, updateYvertices3;
var pivot1;
var parent;
var pointsX = [];
var pointsY = [];
var particleMaterial = new THREE.MeshBasicMaterial({
color: 0x7a7a7a,
transparent: true,
opacity: 0.8
});
init();
animate();
function init() {
scene = new THREE.Scene();
//setup camera for scene
//PerspectiveCamera(fov, aspect, near, far [In terms of camera frustum plane])
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 1000;
//setup renderer for scene (generation of whatever you've made)
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x31AED1, 1);
renderer.setSize( window.innerWidth, window.innerHeight );
//OrbitControls(Camera, HTMLDOMElement)
controls = new THREE.OrbitControls( camera, renderer.domElement );
// Set to true to enable damping (inertia), which can be used to give a sense
//of weight to the controls. Default is false.
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.enableZoom = false;
console.log("Called");
fillSceneWithParticles();
fillSceneWithShapes();
canvas.appendChild( renderer.domElement );
renderer.render( scene, camera );
}
function fillSceneWithParticles() {
var particleGeometry = new THREE.SphereGeometry(3, 32, 32);
parent = new THREE.Object3D();
scene.add(parent);
for (var i = 0; i < particleCount; i++) {
particles[i] = new THREE.Mesh( particleGeometry, particleMaterial );
particles[i].position.x = 0;
particles[i].position.y = 0;
particles[i].position.z = (0);
particles2[i] = new THREE.Mesh( particleGeometry, particleMaterial );
particles2[i].position.x = (200);
particles2[i].position.y = (-115.57);
particles2[i].position.z = (0);
particles3[i] = new THREE.Mesh( particleGeometry, particleMaterial );
particles3[i].position.x = (0);
particles3[i].position.y = (231.84);
particles3[i].position.z = (0);
scene.add(particles[i]);
scene.add(particles2[i]);
scene.add(particles3[i]);
}
}
function fillSceneWithShapes() {
//Add a 2d Triangle W centre = 200, 115.57
var geometry = new THREE.Geometry();
geometry.vertices.push( new THREE.Vector3(-200, -115.57, 0));
geometry.vertices.push( new THREE.Vector3( 200, -115.57, 0 ));
geometry.vertices.push( new THREE.Vector3( 0, 231.84, 0 ));
geometry.vertices.push( new THREE.Vector3( -200, -115.57, 0 ));
var material = new THREE.LineBasicMaterial( { color: 0xffffff, linewidth: 10 } );
line = new THREE.Line( geometry, material );
scene.add(line);
}
function rotateTriangle() {
var geom = line.geometry.clone();
geom.applyMatrix(line.matrix);
updateXvertices = geom.vertices[0].x;
//The circle that we use to place our points
var centerX = 0;
var centerY = 0;
var radius = 231.84;
for(var degree = 90; degree < 450; degree++){
var radians = degree * Math.PI/180;
var x = centerX + radius * Math.cos(radians);
var y = centerY + radius * Math.sin(radians);
pointsX[degree - 90] = x;
pointsY[degree - 90] = y;
}
}
function updatePoints() {
//link counter with number of degrees initially created
//These are intialised because V1 = 120 degrees from V0 and V2 = 240 degrees
var counter = 120;
var counter2 = 240;
var zCounter = 0;
var curveFactor = 1;
var material = new THREE.LineBasicMaterial( { color: 0xffffff, linewidth: 10 } );
var secondTriangle = new THREE.Geometry();
for (var i = 0; i < particleCount; i++) {
parent.add(particles[i]);
//Plot points around the circle relative to vertices of triangle
particles[i].position.x = (pointsX[i]);
particles[i].position.y = (pointsY[i]);
particles[i].position.z = zCounter * curveFactor;
//If array index out of bounds then loop back to the start of array
//i.e. Go back around the circle relative to the triangle vertices
parent.add(particles2[i]);
if (counter == 360) {
counter = 0;
}
particles2[i].position.x = (pointsX[counter]);
particles2[i].position.y = (pointsY[counter]);
particles2[i].position.z = zCounter * curveFactor;
counter++;
if (counter2 == 360) {
counter2 = 0;
}
parent.add(particles3[i]);
particles3[i].position.x = (pointsX[counter2]);
particles3[i].position.y = (pointsY[counter2]);
particles3[i].position.z = zCounter * curveFactor;
counter2++;
zCounter++;
}
//Give the second triangle the position of the last particles in array
secondTriangle.vertices.push( new THREE.Vector3(particles[particleCount-1].position.x, particles[particleCount-1].position.y, particles[particleCount-1].position.z ));
secondTriangle.vertices.push( new THREE.Vector3(particles2[particleCount-1].position.x, particles2[particleCount-1].position.y, particles2[particleCount-1].position.z ));
secondTriangle.vertices.push( new THREE.Vector3(particles3[particleCount-1].position.x, particles3[particleCount-1].position.y, particles3[particleCount-1].position.z ));
secondTriangle.vertices.push( new THREE.Vector3(particles[particleCount-1].position.x, particles[particleCount-1].position.y, particles[particleCount-1].position.z ));
line1 = new THREE.Line( secondTriangle, material );
scene.add(line1);
parent.add(line1);
}
function animate() {
requestAnimationFrame( animate );
controls.update();
rotateTriangle();
updatePoints();
line1.rotation.z -= SPEED *2;
line.rotation.z -= SPEED *2;
parent.rotation.z -= SPEED *2;
renderer.render( scene, camera );
}
In retrospect it seems obvious what the problem was.
Since I had geometry.vertices.push inside my animate loop it was continuously pushing new Vectors to a buffer.
I just had to move the pushing of those vertices and that solved any frame rate and memory problems I was having

ThreeJS Object rotation

Two questions:
1) Why is it so hard to find an example where a 3D object is freely rotating according to mouse movement, for example, if I were to do the following for an object:
object.rotation.x += 1;
object.rotation.y += 1;
The object will not always rotate at the same degree, this seems to be a mathematical problem, does someone have an example or a mathematical explaination for how rotation works, I believe there's something that needs to be implemented for the change in the center of geometry.
When an object has more than 2 center of geometries, for example in the case of an object with different morph targets, why is there only one bounding box, is there an easy way to find the center of geometry for the bounding box at each morph target instead of just an average center of geometry?
Not sure what you're going for, but is it something like this:
https://jsfiddle.net/72p3z0Lh/27/
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var screenX = 0;
var screenY = 0;
var deltaX = 0;
var deltaY = 0;
renderer.domElement.addEventListener('mousemove', function(event) {
deltaX = event.screenX - screenX;
deltaY = event.screenY - screenY;
screenX = event.screenX;
screenY = event.screenY;
});
var geometry = new THREE.BoxGeometry(1, 1, 1);
var material = new THREE.MeshBasicMaterial({
color: 0x00ff00
});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.x = 5;
camera.position.y= 2;
camera.lookAt(new THREE.Vector3(0,0,0));
cube.geometry.translate( -.5, 0, 0 ); //Center Offset
var render = function() {
requestAnimationFrame(render);
cube.rotateOnAxis(cube.worldToLocal(camera.up.clone()).normalize(), deltaX / 20);
cube.rotateOnAxis(cube.worldToLocal(camera.getWorldDirection().clone().cross(camera.up)).normalize(), deltaY / 20);
deltaX = 0;
deltaY = 0;
renderer.render(scene, camera);
};
render();

Drawing rectangle underneath PIXI.Text

In a word game I am trying to draw score as white numbers above a blue (or red) rectangle:
For example, in the above screenshot it is the number "13".
Here is my entire class Score.js (with currently hardcoded WIDTH and HEIGHT):
"use strict";
function Score(color) {
PIXI.Container.call(this);
this.interactive = false;
this.buttonMode = false;
this.visible = false;
this.bgGraphics = new PIXI.Graphics();
this.bgGraphics.beginFill(color, 1);
this.bgGraphics.drawRect(0, 0, Score.WIDTH, Score.HEIGHT);
this.bgGraphics.endFill();
this.addChild(this.bgGraphics);
this.text = new PIXI.Text('XXX', {font: '20px Arial', fill: 0xFFFFFF});
this.text.x = 1;
this.text.y = 1;
this.addChild(this.text);
}
Score.prototype = Object.create(PIXI.Container.prototype);
Score.prototype.constructor = Score;
Score.WIDTH = 36;
Score.HEIGHT = 24;
Score.prototype.setText = function(str) {
this.text.text = str;
}
I wonder, how to modify my setText() function, so that a new rectangle is drawn on each call - as a bounding rectangle for the str argument?
I have looked at the PIXI.Text.getBounds() method, but it returns a Matrix and not a Rectangle...
I think you can just use this.text.width. This has historically had some bugs associated with it, but it should be working right in the latest version.

Resources