-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
af6c7e0
commit 265ba3c
Showing
14 changed files
with
809 additions
and
651 deletions.
There are no files selected for viewing
313 changes: 164 additions & 149 deletions
313
src/examples/v8.0.0-rc/advanced/collisionDetection.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,179 +1,194 @@ | ||
import * as PIXI from 'pixi.js'; | ||
import { Application, Assets, Point, Sprite, Texture, Ticker } from 'pixi.js'; | ||
|
||
// Based somewhat on this article by Spicy Yoghurt | ||
// URL for further reading: https://spicyyoghurt.com/tutorials/html5-javascript-game-development/collision-detection-physics | ||
const app = new PIXI.Application({ background: '#111', resizeTo: window }); | ||
|
||
document.body.appendChild(app.view); | ||
|
||
// Options for how objects interact | ||
// How fast the red square moves | ||
const movementSpeed = 0.05; | ||
|
||
// Strength of the impulse push between two objects | ||
const impulsePower = 5; | ||
|
||
// Test For Hit | ||
// A basic AABB check between two different squares | ||
function testForAABB(object1, object2) | ||
(async () => | ||
{ | ||
const bounds1 = object1.getBounds(); | ||
const bounds2 = object2.getBounds(); | ||
|
||
return ( | ||
bounds1.x < bounds2.x + bounds2.width | ||
&& bounds1.x + bounds1.width > bounds2.x | ||
&& bounds1.y < bounds2.y + bounds2.height | ||
&& bounds1.y + bounds1.height > bounds2.y | ||
); | ||
} | ||
|
||
// Calculates the results of a collision, allowing us to give an impulse that | ||
// shoves objects apart | ||
function collisionResponse(object1, object2) | ||
{ | ||
if (!object1 || !object2) | ||
{ | ||
return new PIXI.Point(0); | ||
} | ||
|
||
const vCollision = new PIXI.Point(object2.x - object1.x, object2.y - object1.y); | ||
// Create a new application | ||
const app = new Application(); | ||
|
||
const distance = Math.sqrt( | ||
(object2.x - object1.x) * (object2.x - object1.x) + (object2.y - object1.y) * (object2.y - object1.y), | ||
); | ||
// Initialize the application | ||
await app.init({ background: '#111', resizeTo: window }); | ||
|
||
const vCollisionNorm = new PIXI.Point(vCollision.x / distance, vCollision.y / distance); | ||
// Append the application canvas to the document body | ||
document.body.appendChild(app.canvas); | ||
|
||
const vRelativeVelocity = new PIXI.Point( | ||
object1.acceleration.x - object2.acceleration.x, | ||
object1.acceleration.y - object2.acceleration.y, | ||
); | ||
// Options for how objects interact | ||
// How fast the red square moves | ||
const movementSpeed = 0.05; | ||
|
||
const speed = vRelativeVelocity.x * vCollisionNorm.x + vRelativeVelocity.y * vCollisionNorm.y; | ||
// Strength of the impulse push between two objects | ||
const impulsePower = 5; | ||
|
||
const impulse = (impulsePower * speed) / (object1.mass + object2.mass); | ||
|
||
return new PIXI.Point(impulse * vCollisionNorm.x, impulse * vCollisionNorm.y); | ||
} | ||
|
||
// Calculate the distance between two given points | ||
function distanceBetweenTwoPoints(p1, p2) | ||
{ | ||
const a = p1.x - p2.x; | ||
const b = p1.y - p2.y; | ||
// Test For Hit | ||
// A basic AABB check between two different squares | ||
function testForAABB(object1, object2) | ||
{ | ||
const bounds1 = object1.getBounds(); | ||
const bounds2 = object2.getBounds(); | ||
|
||
return ( | ||
bounds1.x < bounds2.x + bounds2.width | ||
&& bounds1.x + bounds1.width > bounds2.x | ||
&& bounds1.y < bounds2.y + bounds2.height | ||
&& bounds1.y + bounds1.height > bounds2.y | ||
); | ||
} | ||
|
||
return Math.hypot(a, b); | ||
} | ||
// Calculates the results of a collision, allowing us to give an impulse that | ||
// shoves objects apart | ||
function collisionResponse(object1, object2) | ||
{ | ||
if (!object1 || !object2) | ||
{ | ||
return new Point(0); | ||
} | ||
|
||
// The green square we will knock about | ||
const greenSquare = new PIXI.Sprite(PIXI.Texture.WHITE); | ||
const vCollision = new Point(object2.x - object1.x, object2.y - object1.y); | ||
|
||
greenSquare.position.set((app.screen.width - 100) / 2, (app.screen.height - 100) / 2); | ||
greenSquare.width = 100; | ||
greenSquare.height = 100; | ||
greenSquare.tint = 0x00ff00; | ||
greenSquare.acceleration = new PIXI.Point(0); | ||
greenSquare.mass = 3; | ||
const distance = Math.sqrt( | ||
(object2.x - object1.x) * (object2.x - object1.x) + (object2.y - object1.y) * (object2.y - object1.y), | ||
); | ||
|
||
// The square you move around | ||
const redSquare = new PIXI.Sprite(PIXI.Texture.WHITE); | ||
const vCollisionNorm = new Point(vCollision.x / distance, vCollision.y / distance); | ||
|
||
redSquare.position.set(0, 0); | ||
redSquare.width = 100; | ||
redSquare.height = 100; | ||
redSquare.tint = 0xff0000; | ||
redSquare.acceleration = new PIXI.Point(0); | ||
redSquare.mass = 1; | ||
const vRelativeVelocity = new Point( | ||
object1.acceleration.x - object2.acceleration.x, | ||
object1.acceleration.y - object2.acceleration.y, | ||
); | ||
|
||
const mouseCoords = { x: 0, y: 0 }; | ||
const speed = vRelativeVelocity.x * vCollisionNorm.x + vRelativeVelocity.y * vCollisionNorm.y; | ||
|
||
app.stage.eventMode = 'static'; | ||
app.stage.hitArea = app.screen; | ||
app.stage.on('mousemove', (event) => | ||
{ | ||
mouseCoords.x = event.global.x; | ||
mouseCoords.y = event.global.y; | ||
}); | ||
const impulse = (impulsePower * speed) / (object1.mass + object2.mass); | ||
|
||
// Listen for animate update | ||
app.ticker.add((delta) => | ||
{ | ||
// Applied deacceleration for both squares, done by reducing the | ||
// acceleration by 0.01% of the acceleration every loop | ||
redSquare.acceleration.set(redSquare.acceleration.x * 0.99, redSquare.acceleration.y * 0.99); | ||
greenSquare.acceleration.set(greenSquare.acceleration.x * 0.99, greenSquare.acceleration.y * 0.99); | ||
|
||
// Check whether the green square ever moves off the screen | ||
// If so, reverse acceleration in that direction | ||
if (greenSquare.x < 0 || greenSquare.x > app.screen.width - 100) | ||
{ | ||
greenSquare.acceleration.x = -greenSquare.acceleration.x; | ||
return new Point(impulse * vCollisionNorm.x, impulse * vCollisionNorm.y); | ||
} | ||
|
||
if (greenSquare.y < 0 || greenSquare.y > app.screen.height - 100) | ||
// Calculate the distance between two given points | ||
function distanceBetweenTwoPoints(p1, p2) | ||
{ | ||
greenSquare.acceleration.y = -greenSquare.acceleration.y; | ||
} | ||
const a = p1.x - p2.x; | ||
const b = p1.y - p2.y; | ||
|
||
// If the green square pops out of the cordon, it pops back into the | ||
// middle | ||
if ( | ||
greenSquare.x < -30 | ||
|| greenSquare.x > app.screen.width + 30 | ||
|| greenSquare.y < -30 | ||
|| greenSquare.y > app.screen.height + 30 | ||
) | ||
{ | ||
greenSquare.position.set((app.screen.width - 100) / 2, (app.screen.height - 100) / 2); | ||
return Math.hypot(a, b); | ||
} | ||
|
||
// If the mouse is off screen, then don't update any further | ||
if (app.screen.width > mouseCoords.x || mouseCoords.x > 0 || app.screen.height > mouseCoords.y || mouseCoords.y > 0) | ||
{ | ||
// Get the red square's center point | ||
const redSquareCenterPosition = new PIXI.Point( | ||
redSquare.x + redSquare.width * 0.5, | ||
redSquare.y + redSquare.height * 0.5, | ||
); | ||
// The green square we will knock about | ||
const greenSquare = new Sprite(Texture.WHITE); | ||
|
||
// Calculate the direction vector between the mouse pointer and | ||
// the red square | ||
const toMouseDirection = new PIXI.Point( | ||
mouseCoords.x - redSquareCenterPosition.x, | ||
mouseCoords.y - redSquareCenterPosition.y, | ||
); | ||
greenSquare.position.set((app.screen.width - 100) / 2, (app.screen.height - 100) / 2); | ||
greenSquare.width = 100; | ||
greenSquare.height = 100; | ||
greenSquare.tint = 0x00ff00; | ||
greenSquare.acceleration = new Point(0); | ||
greenSquare.mass = 3; | ||
|
||
// Use the above to figure out the angle that direction has | ||
const angleToMouse = Math.atan2(toMouseDirection.y, toMouseDirection.x); | ||
// The square you move around | ||
const redSquare = new Sprite(Texture.WHITE); | ||
|
||
// Figure out the speed the square should be travelling by, as a | ||
// function of how far away from the mouse pointer the red square is | ||
const distMouseRedSquare = distanceBetweenTwoPoints(mouseCoords, redSquareCenterPosition); | ||
const redSpeed = distMouseRedSquare * movementSpeed; | ||
redSquare.position.set(0, 0); | ||
redSquare.width = 100; | ||
redSquare.height = 100; | ||
redSquare.tint = 0xff0000; | ||
redSquare.acceleration = new Point(0); | ||
redSquare.mass = 1; | ||
|
||
// Calculate the acceleration of the red square | ||
redSquare.acceleration.set(Math.cos(angleToMouse) * redSpeed, Math.sin(angleToMouse) * redSpeed); | ||
} | ||
const mouseCoords = { x: 0, y: 0 }; | ||
|
||
// If the two squares are colliding | ||
if (testForAABB(greenSquare, redSquare)) | ||
app.stage.eventMode = 'static'; | ||
app.stage.hitArea = app.screen; | ||
app.stage.on('mousemove', (event) => | ||
{ | ||
// Calculate the changes in acceleration that should be made between | ||
// each square as a result of the collision | ||
const collisionPush = collisionResponse(greenSquare, redSquare); | ||
// Set the changes in acceleration for both squares | ||
|
||
redSquare.acceleration.set(collisionPush.x * greenSquare.mass, collisionPush.y * greenSquare.mass); | ||
greenSquare.acceleration.set(-(collisionPush.x * redSquare.mass), -(collisionPush.y * redSquare.mass)); | ||
} | ||
mouseCoords.x = event.global.x; | ||
mouseCoords.y = event.global.y; | ||
}); | ||
|
||
greenSquare.x += greenSquare.acceleration.x * delta; | ||
greenSquare.y += greenSquare.acceleration.y * delta; | ||
|
||
redSquare.x += redSquare.acceleration.x * delta; | ||
redSquare.y += redSquare.acceleration.y * delta; | ||
}); | ||
|
||
// Add to stage | ||
app.stage.addChild(redSquare, greenSquare); | ||
// Listen for animate update | ||
Ticker.shared.add((time) => | ||
{ | ||
const delta = time.deltaTime; | ||
|
||
// Applied deacceleration for both squares, done by reducing the | ||
// acceleration by 0.01% of the acceleration every loop | ||
redSquare.acceleration.set(redSquare.acceleration.x * 0.99, redSquare.acceleration.y * 0.99); | ||
greenSquare.acceleration.set(greenSquare.acceleration.x * 0.99, greenSquare.acceleration.y * 0.99); | ||
|
||
// Check whether the green square ever moves off the screen | ||
// If so, reverse acceleration in that direction | ||
if (greenSquare.x < 0 || greenSquare.x > app.screen.width - 100) | ||
{ | ||
greenSquare.acceleration.x = -greenSquare.acceleration.x; | ||
} | ||
|
||
if (greenSquare.y < 0 || greenSquare.y > app.screen.height - 100) | ||
{ | ||
greenSquare.acceleration.y = -greenSquare.acceleration.y; | ||
} | ||
|
||
// If the green square pops out of the cordon, it pops back into the | ||
// middle | ||
if ( | ||
greenSquare.x < -30 | ||
|| greenSquare.x > app.screen.width + 30 | ||
|| greenSquare.y < -30 | ||
|| greenSquare.y > app.screen.height + 30 | ||
) | ||
{ | ||
greenSquare.position.set((app.screen.width - 100) / 2, (app.screen.height - 100) / 2); | ||
} | ||
|
||
// If the mouse is off screen, then don't update any further | ||
if ( | ||
app.screen.width > mouseCoords.x | ||
|| mouseCoords.x > 0 | ||
|| app.screen.height > mouseCoords.y | ||
|| mouseCoords.y > 0 | ||
) | ||
{ | ||
// Get the red square's center point | ||
const redSquareCenterPosition = new Point( | ||
redSquare.x + redSquare.width * 0.5, | ||
redSquare.y + redSquare.height * 0.5, | ||
); | ||
|
||
// Calculate the direction vector between the mouse pointer and | ||
// the red square | ||
const toMouseDirection = new Point( | ||
mouseCoords.x - redSquareCenterPosition.x, | ||
mouseCoords.y - redSquareCenterPosition.y, | ||
); | ||
|
||
// Use the above to figure out the angle that direction has | ||
const angleToMouse = Math.atan2(toMouseDirection.y, toMouseDirection.x); | ||
|
||
// Figure out the speed the square should be travelling by, as a | ||
// function of how far away from the mouse pointer the red square is | ||
const distMouseRedSquare = distanceBetweenTwoPoints(mouseCoords, redSquareCenterPosition); | ||
const redSpeed = distMouseRedSquare * movementSpeed; | ||
|
||
// Calculate the acceleration of the red square | ||
redSquare.acceleration.set(Math.cos(angleToMouse) * redSpeed, Math.sin(angleToMouse) * redSpeed); | ||
} | ||
|
||
// If the two squares are colliding | ||
if (testForAABB(greenSquare, redSquare)) | ||
{ | ||
// Calculate the changes in acceleration that should be made between | ||
// each square as a result of the collision | ||
const collisionPush = collisionResponse(greenSquare, redSquare); | ||
// Set the changes in acceleration for both squares | ||
|
||
redSquare.acceleration.set(collisionPush.x * greenSquare.mass, collisionPush.y * greenSquare.mass); | ||
greenSquare.acceleration.set(-(collisionPush.x * redSquare.mass), -(collisionPush.y * redSquare.mass)); | ||
} | ||
|
||
greenSquare.x += greenSquare.acceleration.x * delta; | ||
greenSquare.y += greenSquare.acceleration.y * delta; | ||
|
||
redSquare.x += redSquare.acceleration.x * delta; | ||
redSquare.y += redSquare.acceleration.y * delta; | ||
}); | ||
|
||
// Add to stage | ||
app.stage.addChild(redSquare, greenSquare); | ||
})(); |
Oops, something went wrong.