I have a famous engine setup and working with physics / drag etc.
But it appears there are other physics options, BruteForce, SweepAndPrune etc.
I suspect these may be what i need to improve the collision detection between the elements.
I've tried to add the BruteForce collision detection like so:
this.collision = new collision([rightWall, leftWall, topWall, bottomWall]);
But I get an error about an incorrect constraint.
this.simulation.addConstraint(this.collision);
Does anyone have experience with solving this issue?
You can see a demo here:
var famous = famous;
var FamousEngine = famous.core.FamousEngine;
var Camera = famous.components.Camera;
var DOMElement = famous.domRenderables.DOMElement;
var Gravity3D = famous.physics.Gravity3D;
var MountPoint = famous.components.MountPoint;
var PhysicsEngine = famous.physics.PhysicsEngine;
var Position = famous.components.Position;
var Size = famous.components.Size;
var Wall = famous.physics.Wall;
var Sphere = famous.physics.Sphere;
var Vec3 = famous.math.Vec3;
var math = famous.math;
var physics = famous.physics;
var collision = famous.physics.Collision;
var gestures = famous.components.GestureHandler;
var Spring = famous.physics.Spring;
console.log(famous)
var anchor = new Vec3(window.innerWidth / 2, window.innerHeight / 2, 0);
//Create Walls
var rightWall = new Wall({
direction: Wall.LEFT
}).setPosition(window.innerWidth - 20, 0, 0);
var leftWall = new Wall({
direction: Wall.RIGHT
}).setPosition(window.innerWidth + 20, 0, 0);
var topWall = new Wall({
direction: Wall.DOWN
}).setPosition(0, 20, 0);
var bottomWall = new Wall({
direction: Wall.UP
}).setPosition(0, window.innerHeight - 20, 0);
var centerPoint;
function Demo() {
this.scene = FamousEngine.createScene('body');
this.collision = new collision([rightWall, leftWall, topWall, bottomWall]);
console.log(this.collision)
this.simulation = new PhysicsEngine();
this.simulation.setOrigin(0.5, 0.5);
this.simulation.addConstraint(this.collision);
this.items = [];
this.walls = [];
//Create Items
for (var i = 0; i < 10; i++) {
var node = this.scene.addChild();
node.setMountPoint(0.5, 0.5);
var size = new Size(node).setMode(1, 1);
var position = new Position(node);
if (i === 0) {
createLogo.call(this, node, size, position);
}
if (i !== 0 && i !== 9) {
node.id = i;
createSatellites.call(this, node, size, position);
}
if (i === 9) {
node.id = i;
createAlternateShape.call(this, node, size, position);
}
}
//Create Walls
var node = this.scene.addChild();
createWalls(node);
var once = true;
Demo.prototype.onUpdate = function(time) {
this.simulation.update(time);
this.collision.resolve(time, 360)
//Postition walls
var wallPosition = topWall.getPosition();
node.setPosition(wallPosition.x, wallPosition.y);
//Position elements
if (this.items.length > 0) {
for (var i = 0; i < this.items.length; i++) {
if (once) {
console.log(this.items[i][1]._node.moving)
once = false;
}
if (this.items[i][1]._node.moving) {
console.log('moving!')
} else {
var itemPosition = this.simulation.getTransform(this.items[i][0]).position;
this.items[i][1].set(itemPosition[0], itemPosition[1], 0);
}
}
}
FamousEngine.requestUpdateOnNextTick(this);
};
FamousEngine.requestUpdateOnNextTick(this);
}
function createWalls(wallNode) {
wallNode.setSizeMode('absolute', 'absolute', 'absolute').setAbsoluteSize(window.innerWidth, 10, 0);
var wallDOMElement = new DOMElement(wallNode, {
tagName: 'div'
}).setProperty('background-color', 'lightblue');
}
function createLogo(node, size, position) {
node.moving = false;
size.setAbsolute(100, 100);
var mp = new MountPoint(node).set(0.5, 0.5);
var el = new DOMElement(node, {
tagName: 'img',
attributes: {
src: 'http://www.denisboudreau.org/presentations/2014/CSUN/Browser%20Zoom%20and%20Low%20Vision/Comps/ToM_EnsoCircle.png'
}
});
centerPoint = new Sphere({
radius: 50,
mass: 10000,
restrictions: ['xy'],
position: new Vec3(window.innerWidth / 2, window.innerHeight / 2, 0)
});
var spring = new Spring(null, centerPoint, {
stiffness: 95,
period: 0.6,
dampingRatio: 1.0,
anchor: new Vec3(window.innerWidth / 2, window.innerHeight / 2, 0)
});
centerPoint.setVelocity(0, 0, 0);
this.simulation.add(centerPoint, spring);
this.items.push([centerPoint, position]);
this.collision.addTarget(centerPoint);
}
function createAlternateShape(node, size, position) {
node.moving = false;
size.setAbsolute(100, 100);
var el = new DOMElement(node, {
properties: {
'background-color': 'red',
}
});
var box = new physics.Box({
size: [100, 100, 100],
mass: 10,
position: new Vec3(100, 100, 0)
});
// Attach the box to the anchor with a `Spring` force
var spring = new Spring(null, box, {
stiffness: 95,
period: 0.6,
dampingRatio: 1.0,
anchor: new Vec3(window.innerWidth / 2, window.innerHeight / 2, 0)
});
box.setVelocity(0.5, 0.5, 0);
this.simulation.add(box, spring);
this.items.push([box, position]);
this.collision.addTarget(box);
}
function createSatellites(node, size, position, i) {
node.moving = false;
var rand = Math.round(Math.random() * 100 + 30);
size.setAbsolute(rand, rand);
var radius = 100;
var x = Math.floor(Math.random() * radius * 2) - radius;
var y = (Math.round(Math.random()) * 2 - 1) * Math.sqrt(radius * radius - x * x);
var color = 'rgb(' + Math.abs(x) + ',' + Math.abs(Math.round(y)) + ',' + (255 - node.id) + ')';
var el = new DOMElement(node, {
properties: {
'background-color': color,
'border-radius': '50%'
}
});
var satellite = new Sphere({
radius: rand / 2,
mass: 10,
position: new Vec3(x + window.innerWidth / 2, y + window.innerHeight / 2, 0)
});
// Attach the box to the anchor with a `Spring` force
var spring = new Spring(null, satellite, {
stiffness: 95,
period: 0.6,
dampingRatio: 1.0,
anchor: anchor
});
//console.log(color);
// satellite.setVelocity(-y / Math.PI, -x / Math.PI / 2, y / 2);
satellite.setVelocity(0.5, 0.5, 0);
// this.gravity.addTarget(satellite);
this.simulation.add(satellite, spring);
this.items.push([satellite, position]);
this.collision.addTarget(satellite);
//Drag
var nodeGesture = new gestures(node);
nodeGesture.on('drag', function(e, p) {
if (e.status == "move") {
node.moving = true;
}
var currentPos = node.getPosition()
var newPosX = currentPos[0] + e.centerDelta.x
var newPosY = currentPos[1] + e.centerDelta.y
satellite.setPosition(newPosX, newPosY)
node.setPosition(newPosX, newPosY)
if (e.status == "end") {
node.moving = false;
}
});
//event
node.addUIEvent('click');
// node.addComponent({
// onReceive:function(event, payload){
// if(event==='click'){
// // el.setContent('I\'ve been clicked')
//
// }
// }
// })
}
setTimeout(function() {
// Boilerplate
FamousEngine.init();
}, 500);
// App Code
var demo = new Demo();
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Famous :: Seed Project</title>
<link rel="icon" href="favicon.ico?v=1" type="image/x-icon">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0px;
padding: 0px;
}
body {
position: absolute;
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
-webkit-font-smoothing: antialiased;
-webkit-tap-highlight-color: transparent;
-webkit-perspective: 0;
perspective: none;
overflow: hidden;
}
</style>
</head>
<body>
<script src="http://code.famo.us/famous/0.6.2/famous.min.js"></script>
</body>
</html>
This is the solution using BruteForceAABB.
var famous = famous;
var FamousEngine = famous.core.FamousEngine;
var Camera = famous.components.Camera;
var DOMElement = famous.domRenderables.DOMElement;
var Gravity3D = famous.physics.Gravity3D;
var MountPoint = famous.components.MountPoint;
var PhysicsEngine = famous.physics.PhysicsEngine;
var Position = famous.components.Position;
var Size = famous.components.Size;
var Wall = famous.physics.Wall;
var Sphere = famous.physics.Sphere;
var Vec3 = famous.math.Vec3;
var math = famous.math;
var physics = famous.physics;
var collision = famous.physics.Collision;
var gestures = famous.components.GestureHandler;
var Spring = famous.physics.Spring;
console.log(famous)
var anchor = new Vec3(window.innerWidth / 2, window.innerHeight / 2, 0);
//Create Walls
var rightWall = new Wall({
direction: Wall.LEFT
}).setPosition(window.innerWidth - 20, 0, 0);
var leftWall = new Wall({
direction: Wall.RIGHT
}).setPosition(window.innerWidth + 20, 0, 0);
var topWall = new Wall({
direction: Wall.DOWN
}).setPosition(0, 20, 0);
var bottomWall = new Wall({
direction: Wall.UP
}).setPosition(0, window.innerHeight - 20, 0);
var centerPoint;
function Demo() {
this.scene = FamousEngine.createScene('body');
// this.collision = new collision.({broadphase: 'BruteForce'});
var broadPhase = new physics.Collision.BruteForceAABB([rightWall, leftWall, topWall, bottomWall]);
this.collision = new collision([topWall], {
'broadPhase': broadPhase
});
this.simulation = new PhysicsEngine();
this.simulation.setOrigin(0.5, 0.5);
this.simulation.addConstraint(this.collision);
this.items = [];
this.walls = [];
//Create Items
for (var i = 0; i < 10; i++) {
var node = this.scene.addChild();
node.setMountPoint(0.5, 0.5);
var size = new Size(node).setMode(1, 1);
var position = new Position(node);
if (i === 0) {
createLogo.call(this, node, size, position);
}
if (i !== 0 && i !== 9) {
node.id = i;
createSatellites.call(this, node, size, position);
}
if (i === 9) {
node.id = i;
createAlternateShape.call(this, node, size, position);
}
}
//Create Walls
var node = this.scene.addChild();
createWalls(node);
var once = true;
Demo.prototype.onUpdate = function(time) {
this.simulation.update(time);
// this.collision.resolve(time, 360)
//Postition walls
var wallPosition = topWall.getPosition();
node.setPosition(wallPosition.x, wallPosition.y);
//Position elements
if (this.items.length > 0) {
for (var i = 0; i < this.items.length; i++) {
if (once) {
console.log(this.items[i][1]._node.moving)
once = false;
}
if (this.items[i][1]._node.moving) {
console.log('moving!')
} else {
var itemPosition = this.simulation.getTransform(this.items[i][0]).position;
this.items[i][1].set(itemPosition[0], itemPosition[1], 0);
}
}
}
FamousEngine.requestUpdateOnNextTick(this);
};
FamousEngine.requestUpdateOnNextTick(this);
}
function createWalls(wallNode) {
wallNode.setSizeMode('absolute', 'absolute', 'absolute').setAbsoluteSize(window.innerWidth, 10, 0);
var wallDOMElement = new DOMElement(wallNode, {
tagName: 'div'
}).setProperty('background-color', 'lightblue');
}
function createLogo(node, size, position) {
node.moving = false;
size.setAbsolute(100, 100);
var mp = new MountPoint(node).set(0.5, 0.5);
var el = new DOMElement(node, {
tagName: 'img',
attributes: {
src: './images/famous_logo.png'
}
});
centerPoint = new Sphere({
radius: 50,
mass: 100000,
restrictions: ['xy'],
position: new Vec3(window.innerWidth / 2, window.innerHeight / 2, 0)
});
var spring = new Spring(null, centerPoint, {
stiffness: 95,
period: 0.6,
dampingRatio: 1.0,
anchor: new Vec3(window.innerWidth / 2, window.innerHeight / 2, 0)
});
centerPoint.setVelocity(0, 0, 0);
this.simulation.add(centerPoint, spring);
this.items.push([centerPoint, position]);
this.collision.addTarget(centerPoint);
}
function createAlternateShape(node, size, position) {
node.moving = false;
size.setAbsolute(100, 100);
var el = new DOMElement(node, {
properties: {
'background-color': 'red',
}
});
var box = new physics.Box({
size: [101, 101, 101],
mass: 100,
position: new Vec3(100, 100, 0)
});
// Attach the box to the anchor with a `Spring` force
var spring = new Spring(null, box, {
period: 1.5,
dampingRatio: 0.8,
anchor: new Vec3(window.innerWidth / 2, window.innerHeight / 2, 0)
});
box.setVelocity(0.1, 0.1, 0);
this.simulation.add(box, spring);
this.items.push([box, position]);
this.collision.addTarget(box);
}
function createSatellites(node, size, position, i) {
node.moving = false;
var rand = Math.round(Math.random() * 100 + 30);
size.setAbsolute(rand, rand);
var radius = rand;
var x = Math.floor(Math.random() * radius * 2) - radius;
var y = (Math.round(Math.random()) * 2 - 1) * Math.sqrt(radius * radius - x * x);
var color = 'rgb(' + Math.abs(x) + ',' + Math.abs(Math.round(y)) + ',' + (255 - node.id) + ')';
var el = new DOMElement(node, {
properties: {
'background-color': color,
'border-radius': '50%'
}
});
var satellite = new Sphere({
radius: rand / 2,
mass: 100,
position: new Vec3(x + window.innerWidth / 2, y + window.innerHeight / 2, 0)
});
// Attach the box to the anchor with a `Spring` force
var spring = new Spring(null, satellite, {
// stiffness: 10,
period: 1.5,
dampingRatio: 0.8,
anchor: anchor
});
//console.log(color);
// satellite.setVelocity(-y / Math.PI, -x / Math.PI / 2, y / 2);
satellite.setVelocity(0.1, 0.1, 0);
// this.gravity.add(satellite);
this.simulation.add(satellite, spring);
this.items.push([satellite, position]);
this.collision.addTarget(satellite);
//Drag
var nodeGesture = new gestures(node);
nodeGesture.on('drag', function(e, p) {
if (e.status == "move") {
node.moving = true;
}
var currentPos = node.getPosition()
var newPosX = currentPos[0] + e.centerDelta.x
var newPosY = currentPos[1] + e.centerDelta.y
satellite.setPosition(newPosX, newPosY)
node.setPosition(newPosX, newPosY)
if (e.status == "end") {
node.moving = false;
}
});
//event
node.addUIEvent('click');
// node.addComponent({
// onReceive:function(event, payload){
// if(event==='click'){
// // el.setContent('I\'ve been clicked')
//
// }
// }
// })
}
setTimeout(function() {
// Boilerplate
FamousEngine.init();
}, 500);
// App Code
var demo = new Demo();
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Famous :: Seed Project</title>
<link rel="icon" href="favicon.ico?v=1" type="image/x-icon">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0px;
padding: 0px;
}
body {
position: absolute;
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
-webkit-font-smoothing: antialiased;
-webkit-tap-highlight-color: transparent;
-webkit-perspective: 0;
perspective: none;
overflow: hidden;
}
</style>
</head>
<body>
<script src="http://code.famo.us/famous/0.6.2/famous.min.js"></script>
</body>
</html>
Related
I want to draw line, where it's first pair of coordinates situated in the center of drew circle and it's second pair will be unsettled until I direct it to the cirlce with the same color, which have first one. Do you have any ideas?
My code:
let config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#f0ebeb',
physics: {
default: 'arcade',
arcade: {
gravity: { y: 300 },
debug: false
}
},
scene: {
preload: preload,
create: create,
update: update
},
scale: {
autoCenter: Phaser.Scale.CENTER_BOTH
}
};
let game = new Phaser.Game(config);
let items = [];
let dots = new Map([
[1, '#4293f5'],
[2, '#42f554'],
[3, '#f5e942'],
[4, '#f55a42'],
[5, '#f542c8'],
])
function preload() {
}
function create() {
let x = 100;
let y = 0;
for (i = 0; i < 36; i++) {
if (i % 6 === 0) {
y += 85;
x = 100;
}
this.add.circle(x, y, 35, parseInt(dots.get(getRandomInt(5)).replace(/^#/, ''), 16));
x += 125;
}
}
function update() { }
function getRandomInt(max) {
return Math.floor(Math.random() * max) + 1;
}
What I am trying to do: https://play.google.com/store/apps/details?id=com.nerdyoctopus.gamedots
A quick possible solution could be to use the pointer Events (pointerdown, pointermove and pointerup) of the scene. Here is a demo code, with the basic functionality
let config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 400,
height: 200,
scene: { create }
};
let game = new Phaser.Game(config);
let isDragging = false;
let lineStartPosition = {x:0 , y:0};
let line;
function create ()
{
let cicles = []
for(let rowIdx = 0; rowIdx < 4; rowIdx++ ){
for(let colIdx = 0; colIdx < 2; colIdx++ ){
let circle = this.add.circle(50 + 100 * rowIdx, 50 + 100 * colIdx, 25, 0x6666ff).setOrigin(.5);
circle.setInteractive();
cicles.push(circle);
}
}
line = this.add.line(0,0, 0,0, 100, 100, 0xffffff).setOrigin(0);
line.setLineWidth(5);
line.visible = false;
// adding the events to the scene
this.input.on('pointerdown', dragStart);
this.input.on('pointerup', dragEnd);
this.input.on('pointermove', drag);
}
function dragStart(pointer, gameObjects){
if(gameObjects.length == 0)
return
lineStartPosition.x = gameObjects[0].x;
lineStartPosition.y = gameObjects[0].y;
isDragging = true;
line.x = gameObjects[0].x;
line.y = gameObjects[0].y;
line.setTo(0, 0, 0, 0);
line.visible = true;
}
function drag(pointer, gameObject){
if(isDragging == true){
line.setTo(0, 0, pointer.x - lineStartPosition.x, pointer.y - lineStartPosition.y);
}
}
function dragEnd(pointer, gameObject){
isDragging = false;
}
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
I'm using Fabric JS. Every time I press the Add Door button it creates on the upper left. I gave the values in the rectangle (left: 40, top: 40,).
However, when I press the button while I am on the area I zoomed, I want it to add it to the area I zoomed. I looked at a few examples but could not find what I wanted. How can I do that?
Video
var canvas = new fabric.Canvas('c');
canvas.setBackgroundImage('https://i.hizliresim.com/0pIPiv.jpg', canvas.renderAll.bind(canvas));
var uniqids = 0;
$("#door").on("click", function(e) {
rect = new fabric.Rect({
id:uniqid,
left: 40,
top: 40,
width: 35,
height: 50,
fill: 'blue',
stroke: 'blue',
strokeWidth: 5,
strokeUniform: false,
hasControls : true,
});
var uniqid = uniqids.toString();
var text = new fabric.Text(uniqid, {
fontSize: 30,
originX: 'center',
originY: 'right'
});
var group = new fabric.Group([ rect, text ], {
left: 0,
top: 100,
});
canvas.add(group);
uniqids++;
canvas.on('selection:cleared', c => {
console.log("empty");
});
canvas.selection = false;
});
//*****************************
// canvas.on('mouse:wheel', function(opt) {
// var delta = opt.e.deltaY;
// var zoom = canvas.getZoom();
// zoom *= 0.999 ** delta;
// if (zoom > 20) zoom = 20;
// if (zoom < 0.01) zoom = 0.01;
// canvas.setZoom(zoom);
// opt.e.preventDefault();
// opt.e.stopPropagation();
// })
$('#getid').click(function() {
var activeObject = canvas.getActiveObjects();
alert(canvas.getActiveObject().id);
});
//***************************************
$("#save").on("click", function(e) {
$(".save").html(canvas.toSVG());
});
$('#delete').click(function() {
var activeObject = canvas.getActiveObjects();
canvas.discardActiveObject();
canvas.remove(...activeObject);
});
$("#btnResetZoom").on("click", function(e) {
canvas.setViewportTransform([1,0,0,1,0,0]);
});
canvas.on('mouse:wheel', function(opt) {
var delta = opt.e.deltaY;
var zoom = canvas.getZoom();
zoom *= 0.999 ** delta;
if (zoom > 20) zoom = 20;
if (zoom < 1) zoom = 1;
canvas.zoomToPoint({x: opt.e.offsetX, y: opt.e.offsetY}, zoom);
opt.e.preventDefault();
opt.e.stopPropagation();
});
var shiftKeyDown = true;
var mouseDownPoint = null;
canvas.on('mouse:move', function(options) {
if (shiftKeyDown && mouseDownPoint) {
var pointer = canvas.getPointer(options.e, true);
var mouseMovePoint = new fabric.Point(pointer.x, pointer.y);
canvas.relativePan(mouseMovePoint.subtract(mouseDownPoint));
mouseDownPoint = mouseMovePoint;
keepPositionInBounds(canvas);
}
});
var Direction = {
LEFT: 0,
UP: 1,
RIGHT: 2,
DOWN: 3
};
var zoomLevel = 0;
var zoomLevelMin = 0;
var zoomLevelMax = 3;
var shiftKeyDown = false;
var mouseDownPoint = null;
canvas.on('mouse:down', function(options) {
var pointer = canvas.getPointer(options.e, true);
mouseDownPoint = new fabric.Point(pointer.x, pointer.y);
});
canvas.on('mouse:up', function(options) {
mouseDownPoint = null;
});
canvas.on('mouse:move', function(options) {
if (shiftKeyDown && mouseDownPoint) {
var pointer = canvas.getPointer(options.e, true);
var mouseMovePoint = new fabric.Point(pointer.x, pointer.y);
canvas.relativePan(mouseMovePoint.subtract(mouseDownPoint));
mouseDownPoint = mouseMovePoint;
keepPositionInBounds(canvas);
}
});
fabric.util.addListener(document.body, 'keydown', function(options) {
if (options.repeat) {
return;
}
var key = options.which || options.keyCode; // key detection
if (key == 16) { // handle Shift key
canvas.defaultCursor = 'move';
canvas.selection = false;
shiftKeyDown = true;
} else if (key === 37) { // handle Left key
move(Direction.LEFT);
} else if (key === 38) { // handle Up key
move(Direction.UP);
} else if (key === 39) { // handle Right key
move(Direction.RIGHT);
} else if (key === 40) { // handle Down key
move(Direction.DOWN);
}
});
fabric.util.addListener(document.body, 'keyup', function(options) {
var key = options.which || options.keyCode; // key detection
if (key == 16) { // handle Shift key
canvas.defaultCursor = 'default';
canvas.selection = true;
shiftKeyDown = false;
}
});
// jQuery('.canvas-container').on('mousewheel', function(options) {
// var delta = options.originalEvent.wheelDelta;
// if (delta != 0) {
// var pointer = canvas.getPointer(options.e, true);
// var point = new fabric.Point(pointer.x, pointer.y);
// if (delta > 0) {
// zoomIn(point);
// } else if (delta < 0) {
// zoomOut(point);
// }
// }
// });
function move(direction) {
switch (direction) {
case Direction.LEFT:
canvas.relativePan(new fabric.Point(-10 * canvas.getZoom(), 0));
break;
case Direction.UP:
canvas.relativePan(new fabric.Point(0, -10 * canvas.getZoom()));
break;
case Direction.RIGHT:
canvas.relativePan(new fabric.Point(10 * canvas.getZoom(), 0));
break;
case Direction.DOWN:
canvas.relativePan(new fabric.Point(0, 10 * canvas.getZoom()));
break;
}
keepPositionInBounds(canvas);
}
// function zoomIn(point) {
// if (zoomLevel < zoomLevelMax) {
// zoomLevel++;
// canvas.zoomToPoint(point, Math.pow(2, zoomLevel));
// keepPositionInBounds(canvas);
// }
// }
// function zoomOut(point) {
// console.log(zoomLevel, zoomLevelMin);
// if (zoomLevel > zoomLevelMin) {
// zoomLevel--;
// canvas.zoomToPoint(point, Math.pow(2, zoomLevel));
// keepPositionInBounds(canvas);
// }
// }
function keepPositionInBounds() {
var zoom = canvas.getZoom();
var xMin = (2 - zoom) * canvas.getWidth() / 2;
var xMax = zoom * canvas.getWidth() / 2;
var yMin = (2 - zoom) * canvas.getHeight() / 2;
var yMax = zoom * canvas.getHeight() / 2;
var point = new fabric.Point(canvas.getWidth() / 2, canvas.getHeight() / 2);
var center = fabric.util.transformPoint(point, canvas.viewportTransform);
var clampedCenterX = clamp(center.x, xMin, xMax);
var clampedCenterY = clamp(center.y, yMin, yMax);
var diffX = clampedCenterX - center.x;
var diffY = clampedCenterY - center.y;
if (diffX != 0 || diffY != 0) {
canvas.relativePan(new fabric.Point(diffX, diffY));
}
}
function clamp(value, min, max) {
return Math.max(min, Math.min(value, max));
}
#c {
background-color: grey;
margin-top: 10px;
}
button {
padding: 10px 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.1.0/fabric.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<button id="door">Door</button>
<button id="delete">Delete Door</button>
<button id="save">Save</button>
<button id="getid">GET ID</button>
<button id="btnResetZoom">Reset Zoom</button>
<canvas id="c" width="800" height="800"></canvas>
<br>
<p class="save">
</p>
I think you should use the transformPoint method to translate position
$("#door").on("click", function (e) {
const points = {};
const iVpt = fabric.util.invertTransform(canvas.viewportTransform);
points.tl = fabric.util.transformPoint({x: 40, y: 40}, iVpt);
rect = new fabric.Rect({
id: uniqid,
left: points.tl.x,
top: points.tl.y,
width: 35,
height: 50,
fill: "blue",
stroke: "blue",
strokeWidth: 5,
strokeUniform: false,
hasControls: true,
});
My question is for this plugin: https://codepen.io/bferioli/pen/qEGaPp
I'm trying to use several pictures in this canvas but I can't find how to. Basically, what I want is to display randomly a selection of images (10 or more) instead of only the heart.
I think I have to edit this part of that CodePen snippet:
heartHeight: 60,
heartWidth: 64,
hearts: [],
heartImage: 'http://i58.tinypic.com/ntnw5.png',
maxHearts: 8,
minScale: 0.4,
draw: function() {
this.setCanvasSize();
this.ctx.clearRect(0, 0, this.w, this.h);
for (var i = 0; i < this.hearts.length; i++) {
var heart = this.hearts[i];
heart.image = new Image();
heart.image.style.height = heart.height;
heart.image.src = this.heartImage;
this.ctx.globalAlpha = heart.opacity;
this.ctx.drawImage (heart.image, heart.x, heart.y, heart.width,
heart.height);
}
HERE IS MY CODE visible here: https://codepen.io/Le-future/pen/eKaarK
var imagesArray = ["data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAABlBMVEX/4AL9+dVNpYF7AAAATElEQVR4nO3BQREAAAwCIO1fein28YB0XQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAvXXdlRQHDJgU7pgAAAABJRU5ErkJggg==", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAA1BMVEUpczS1QJ41AAAASElEQVR4nO3BgQAAAADDoPlTX+AIVQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwDcaiAAFXD1ujAAAAAElFTkSuQmCC", "https://www.apyart.com/2226-thickbox_default/bleu-ciel-500ml.jpg"];
var HeartsBackground = {
heartHeight: 60,
heartWidth: 64,
hearts: [],
heartImage: 'http://i58.tinypic.com/ntnw5.png',
maxHearts: 8,
minScale: 0.4,
draw: function() {
this.setCanvasSize();
this.ctx.clearRect(0, 0, this.w, this.h);
for (var i = 0; i < this.hearts.length; i++) {
var num = Math.floor(Math.random() * 3); // 0...3
var heart = this.hearts[i];
heart.image = new Image();
heart.image.style.height = heart.height;
heart.image.src = imagesArray[num];
this.ctx.globalAlpha = heart.opacity;
this.ctx.drawImage (heart.image, heart.x, heart.y, heart.width, heart.height);
}
this.move();
},
move: function() {
for(var b = 0; b < this.hearts.length; b++) {
var heart = this.hearts[b];
heart.y += heart.ys;
if(heart.y > this.h) {
heart.x = Math.random() * this.w;
heart.y = -1 * this.heartHeight;
}
}
},
setCanvasSize: function() {
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight;
this.w = this.canvas.width;
this.h = this.canvas.height;
},
initialize: function() {
this.canvas = $('#canvas')[0];
if(!this.canvas.getContext)
return;
this.setCanvasSize();
this.ctx = this.canvas.getContext('2d');
for(var a = 0; a < this.maxHearts; a++) {
var scale = (Math.random() * (1 - this.minScale)) + this.minScale;
this.hearts.push({
x: Math.random() * this.w,
y: Math.random() * this.h,
ys: Math.random() + 1,
height: scale * this.heartHeight,
width: scale * this.heartWidth,
opacity: scale,
image: imagesArray[Math.floor(Math.random()*imagesArray.length)]
});
}
setInterval($.proxy(this.draw, this), 30);
}
};
$(document).ready(function(){
HeartsBackground.initialize();
});
My answer is to add an array outside of the heartsBackground loop. it will be used to store the image of each of the 8 objects.
var heartsOutside = ["","","","","","","",""]
I then check to see if the outside array contains data and load if exists
if(heartsOutside[i] != ""){num = heartsOutside[i]}
Then store the data to the outside array for the next loop
heartsOutside[i] = num;
I also added a step to clear the heart image upon reaching the bottom of the fall in the move function
heartsOutside[b] = ""
var imagesArray = ["data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAABlBMVEX/4AL9+dVNpYF7AAAATElEQVR4nO3BQREAAAwCIO1fein28YB0XQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAvXXdlRQHDJgU7pgAAAABJRU5ErkJggg==", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAA1BMVEUpczS1QJ41AAAASElEQVR4nO3BgQAAAADDoPlTX+AIVQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwDcaiAAFXD1ujAAAAAElFTkSuQmCC", "https://www.apyart.com/2226-thickbox_default/bleu-ciel-500ml.jpg"];
var heartsOutside = ["","","","","","","",""]
var HeartsBackground = {
heartHeight: 60,
heartWidth: 64,
hearts: [],
heartImage: 'http://i58.tinypic.com/ntnw5.png',
maxHearts: 8,
minScale: 0.4,
draw: function() {
this.setCanvasSize();
this.ctx.clearRect(0, 0, this.w, this.h);
for (var i = 0; i < this.hearts.length; i++) {
//else{
var num = Math.floor(Math.random() * 3); // 0...4
if(heartsOutside[i] != ""){num = heartsOutside[i]}
var heart = this.hearts[i];
heartsOutside[i] = num;
heart.image = new Image();
heart.image.style.height = heart.height;
heart.image.src = imagesArray[num];
this.ctx.globalAlpha = heart.opacity;
this.ctx.drawImage (heart.image, heart.x, heart.y, heart.width, heart.height);
}
this.move();
},
move: function() {
for(var b = 0; b < this.hearts.length; b++) {
var heart = this.hearts[b];
heart.y += heart.ys;
if(heart.y > this.h) {
heart.x = Math.random() * this.w;
heart.y = -1 * this.heartHeight;
heartsOutside[b] = ""
}
}
},
setCanvasSize: function() {
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight;
this.w = this.canvas.width;
this.h = this.canvas.height;
},
initialize: function() {
this.canvas = $('#canvas')[0];
if(!this.canvas.getContext)
return;
this.setCanvasSize();
this.ctx = this.canvas.getContext('2d');
for(var a = 0; a < this.maxHearts; a++) {
var scale = (Math.random() * (1 - this.minScale)) + this.minScale;
this.hearts.push({
x: Math.random() * this.w,
y: Math.random() * this.h,
ys: Math.random() + 1,
height: scale * this.heartHeight,
width: scale * this.heartWidth,
opacity: scale,
image: imagesArray[Math.floor(Math.random()*imagesArray.length)]
});
}
setInterval($.proxy(this.draw, this), 30);
}
};
$(document).ready(function(){
HeartsBackground.initialize();
});
body {
background: #B7004E;
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas"></canvas>
I got an agar.io clone from online and I am trying to remove the Quadtree in the corner (acts as a map) completely! I try to delete everything associating with the quadtree but i end up with a gray screen. Could someone please remove the map from the top right corner for me or tell me which lines to remove?
Here is my HTML:
<div id="viewport">
<canvas id="canvas1"></canvas>
<canvas id="canvas2"></canvas>
<canvas id="canvas3" width="250px" height="250px"></canvas>
</div>
Here is my javascript:
var MouseHandler = (function() {
var x = 0;
var y = 0;
var mouseIn = false;
var init = function(eventSrc) {
eventSrc.addEventListener('mousemove', onMouseMove);
eventSrc.addEventListener('mouseout', onMouseOut);
eventSrc.addEventListener('mouseover', onMouseOver);
};
var onMouseOut = function() {
mouseIn = false;
};
var onMouseOver = function() {
mouseIn = true;
};
var onMouseMove = function(e) {
x = e.clientX;
y = e.clientY;
};
var getPos = function() {
return {
x: x,
y: y
};
};
var isMouseIn = function() {
return mouseIn;
};
return {
init: init,
getPos: getPos,
isMouseIn: isMouseIn
};
}());
Quadtree.MAX_OBJECTS = 5;
Quadtree.MAX_LEVEL = 5;
function Quadtree(lvl, bnds) {
var level = lvl;
var bounds = bnds;
var objects = [];
var nodes = [];
var xMiddle = bounds.x + (bounds.width / 2);
var yMiddle = bounds.y + (bounds.height / 2);
var clear = function() {
objects = [];
nodes = [];
};
var split = function() {
nodes[0] = new Quadtree(level+1, {x: xMiddle, y: bounds.y , width: bounds.width/2, height: bounds.height/2});
nodes[1] = new Quadtree(level+1, {x: bounds.x, y: bounds.y, width: bounds.width/2, height: bounds.height/2});
nodes[2] = new Quadtree(level+1, {x: bounds.x, y: yMiddle, width: bounds.width/2, height: bounds.height/2});
nodes[3] = new Quadtree(level+1, {x: xMiddle, y: yMiddle, width: bounds.width/2, height: bounds.height/2});
};
var getIndex = function(rec) {
var top = (rec.y > bounds.y && (rec.y+rec.height) < yMiddle);
var bottom = (rec.y > yMiddle && (rec.y+rec.height) < (bounds.y+bounds.height));
if(rec.x > bounds.x && (rec.x+rec.width) < xMiddle) {
if(top) {
return 1;
} else if(bottom) {//LEFT
return 2;
}
} else if(rec.x > xMiddle && (rec.x+rec.width) < (bounds.x+bounds.width)) {
if(top) {
return 0;
} else if(bottom) {//RIGHT
return 3;
}
}
return -1;
};
var insert = function(ent) {
var rec = ent.getBounds();
var index = getIndex(rec);
var len = 0;
var i = 0;
if(nodes[0] && index !== -1) {
nodes[index].insert(ent);
return;
}
objects.push(ent);
if(objects.length > Quadtree.MAX_OBJECTS && level < Quadtree.MAX_LEVEL) {
if(!nodes[0]) {
split();
}
len = objects.length;
while(i < objects.length) {
index = getIndex(objects[i].getBounds());
if(index !== -1) {
nodes[index].insert(objects[i]);
objects.splice(i, 1);
} else {
i += 1;
}
}
}
};
var retrieve = function (list, ent) {
var rec1 = bounds;
var rec2 = ent.getBounds();
if(rec2.x < (rec1.x+rec1.width) && (rec2.x+rec2.width) > rec1.x &&
rec2.y < (rec1.y+rec1.height) && (rec2.y+rec2.height) > rec1.y) {
for(var o in objects) {
if(objects[o] !== ent) {
list.push(objects[o]);
}
}
if(nodes.length) {
nodes[0].retrieve(list, ent);
nodes[1].retrieve(list, ent);
nodes[2].retrieve(list, ent);
nodes[3].retrieve(list, ent);
}
}
return list;
};
var drawTree = function(ctx) {
draw(ctx);
if(nodes[0]) {
nodes[0].drawTree(ctx);
nodes[1].drawTree(ctx);
nodes[2].drawTree(ctx);
nodes[3].drawTree(ctx);
}
};
var draw = function(ctx) {
var entAttr = null
ctx.strokeStyle = 'black';
ctx.lineWidth = 1;
ctx.strokeRect(bounds.x/20, bounds.y/20, bounds.width/20, bounds.height/20);
ctx.fillStyle = 'gray';
for(o in objects) {
entAttr = objects[o].getAttr();
ctx.fillRect(entAttr.x/20, entAttr.y/20, 3, 3);
}
};
var toString = function() {
return '('+bounds.x+','+bounds.y+')'+'['+bounds.width+','+bounds.height+']';
};
return {
clear: clear,
insert: insert,
retrieve: retrieve,
drawTree: drawTree,
toString: toString,
};
}
function Circle(attr) {
attr = attr || {};
var x = attr.x || 0;
var y = attr.y || 0;
var mass = attr.mass || 500;
var color = attr.color || 'white';
var borderColor = attr.borderColor || 'black';
var velX = attr.velX || 0;
var velY = attr.velY || 0;
var droplet = attr.droplet || false;
var maxVel = 80 * (80 / Math.sqrt(mass));
var radius = Math.sqrt( mass / Math.PI ); //1 unit of area === 1 uni of mass
var getAttr = function() {
return {
x: x,
y: y,
mass: mass,
radius: radius,
color: color,
velX: velX,
velY: velY,
maxVel: maxVel
};
};
var setAttr = function(attr) {
x = (attr.x !== undefined)? attr.x: x;
y = (attr.y !== undefined)? attr.y: y;
mass = (attr.mass !== undefined)? attr.mass: mass;
color = (attr.color !== undefined)? attr.color: color;
velX = (attr.velX !== undefined)? attr.velX: velX;
velY = (attr.velY !== undefined)? attr.velY: velY;
};
var incMass = function(dMass) {
mass += dMass;
maxVel = 100 * (100 / Math.sqrt(mass));
radius = Math.sqrt( mass / Math.PI );
};
var intersects = function(ent2) {
var ent2Attr = ent2.getAttr();
var dX = Math.abs(ent2Attr.x - x);
var dY = Math.abs(ent2Attr.y - y);
var totalRadius = ent2Attr.radius + radius;
return (dX < totalRadius && dY < totalRadius);
};
var draw = function(ctx, cam) {
var camSize = cam.getSize();
var rPos = cam.getRelPos(getAttr());
//if outside Cam view
if((rPos.x + radius) < 0 || (rPos.y + radius) < 0 || (rPos.x - radius) > camSize.width || (rPos.y - radius) > camSize.height) {
return;
}
ctx.fillStyle = color;
ctx.strokeStyle = borderColor;
ctx.lineWidth = 5;
ctx.beginPath();
ctx.arc(rPos.x, rPos.y, radius, 0, 2 * Math.PI, false);
ctx.fill();
ctx.stroke();
};
var update = !droplet && function(dTime) {
x += velX * dTime;
y += velY * dTime;
};
var getBounds = function() {
return {
x: x-radius,
y: y-radius,
width: radius*2,
height: radius*2,
};
};
return {
getBounds: getBounds,
getAttr: getAttr,
setAttr: setAttr,
incMass: incMass,
intersects: intersects,
draw: draw,
update: update
};
}
var gameManager = (function() {
var canvasGrid = null;
var canvasEnt = null;
var canvasQuadtree = null;
var ctxGrid = null;
var ctxEnt = null;
var ctxQuadTree = null;
var dTime = 1 / 60;
var player = null;
var entityBag = [];
var qTree = null;
var drwQuad = 0;
var addEntity = function(ent) {
entityBag.push(ent);
};
var removeEntity = function(ent) {
var len = entityBag.length;
var i = 0;
for(i=0; i<len; i+=1) {
if(ent === entityBag[i]) {
entityBag.splice(i, 1);
return;
}
}
};
var init = function(cvsGridId, cvsEntId, cvsQuadtree) {
canvasGrid = document.getElementById(cvsGridId);
canvasEnt = document.getElementById(cvsEntId);
canvasQuadtree = document.getElementById(cvsQuadtree);
ctxGrid = canvasGrid.getContext('2d');
ctxEnt = canvasEnt.getContext('2d');
ctxQuadtree = canvasQuadtree.getContext('2d');
fitToContainer(canvasGrid);
fitToContainer(canvasEnt);
ctxGrid.fillStyle = '#F0FBFF';
ctxGrid.strokeStyle = '#BFBFBF';
ctxGrid.lineWidth = 1;
MouseHandler.init(document);
qTree = new Quadtree(0, {x:0, y:0, width:5000, height:5000});
player = new Circle({
x: 50,
y: 50,
color: 'red',
mass: 1000,
velX: 500,
velY:500
});
Camera.init(ctxEnt, player);
addEntity(player);
gameloop();
};
var handleInput = function() {
if(MouseHandler.isMouseIn() === false) {
player.setAttr({ velX: 0, velY: 0 });
return;
}
var pAttr = player.getAttr();
var rPlyrPos = Camera.getRelPos(player.getAttr());
var mPos = MouseHandler.getPos();
var dX = mPos.x - rPlyrPos.x;
var dY = mPos.y - rPlyrPos.y;
var vLength = Math.sqrt( (dX*dX) + (dY*dY) );
var normX = dX / vLength;
var normY = dY / vLength;
var newVelX = normX * (pAttr.maxVel * vLength / 50);
var newVelY = normY * (pAttr.maxVel * vLength / 50);
player.setAttr({
velX: newVelX,
velY: newVelY
});
};
var drawGrid = function() {
var camPos = Camera.getPos();
var camSize = Camera.getSize();
var start = Math.floor(camPos.x / 40);
var relX = Camera.getRelPos({x: (start*40), y: 0}).x;
var numLines = camSize.width / 40;
var i = 0;
ctxGrid.fillRect(0, 0, canvasGrid.width, canvasGrid.height);
for(i=0; i<numLines; i+=1) {
ctxGrid.beginPath();
ctxGrid.moveTo(relX + (40 * i), 0);
ctxGrid.lineTo(relX + (40 * i), camSize.height);
ctxGrid.stroke();
}
start = Math.floor(camPos.y / 40);
var relY = Camera.getRelPos({x: 0, y: (start * 40)}).y;
numLines = camSize.height / 40;
for(i=0; i<numLines; i+=1) {
ctxGrid.beginPath();
ctxGrid.moveTo(0, relY + (40 * i));
ctxGrid.lineTo(camSize.width, relY + (40 * i));
ctxGrid.stroke();
}
};
var handleCollisions = function() {
var possibleColl = [];
var collisions = [];
var coll = null;
var ent = null;
var plyMass = player.getAttr().mass
var entMass = 0;
qTree.clear();
for(ent in entityBag) {
qTree.insert(entityBag[ent]);
}
possibleColl = qTree.retrieve([], player);
while(ent = possibleColl.pop()) {
if(player.intersects(ent)) {
entMass = ent.getAttr().mass;
if(plyMass > (1.5 * entMass)) {
removeEntity(ent);
player.incMass(entMass);
}
}
var entAttr = ent.getAttr();
ctxQuadtree.fillStyle = 'red';
ctxQuadtree.fillRect(entAttr.x/20, entAttr.y/20, 3, 3);
}
};
var fitToContainer = function(canvas) {
canvas.style.width='100%';
canvas.style.height='100%';
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
};
var gameloop = function() {
var len = entityBag.length;
var i = 0;
var ent = null;
handleInput();
Camera.update();
drawGrid();
if(drwQuad === 5) {
ctxQuadtree.fillStyle = 'white';
ctxQuadtree.fillRect(0, 0, 250, 250);
qTree.drawTree(ctxQuadtree);
drwQuad = 0;
}
drwQuad += 1;
ctxEnt.clearRect(0, 0, canvasEnt.width, canvasEnt.height)
for(i=0; i<len; i+=1) {
ent = entityBag[i];
if(ent.update) {
ent.update(dTime);
}
if(ent.draw) {
ent.draw(ctxEnt, Camera);
}
}
handleCollisions();
setTimeout(gameloop, dTime * 1000);
};
return {
init: init,
addEntity: addEntity,
removeEntity: removeEntity
};
}());
var Camera = (function() {
var x = 0;
var y = 0;
var width = 0;
var height = 0;
var ctx = null;
var player = null;
var init = function(_ctx, plyr) {
ctx = _ctx;
player = plyr;
width = ctx.canvas.width;
height = ctx.canvas.height;
};
var update = function() {
width = ctx.canvas.width;
height = ctx.canvas.height;
var plyrAttr = player.getAttr();
x = (plyrAttr.x - width / 2);
y = (plyrAttr.y - height / 2);
};
var getRelPos = function(entAttr) {
var relX = entAttr.x - x;
var relY = entAttr.y - y;
return {
x: relX,
y: relY
};
};
var getPos = function() {
return {
x: x,
y: y
};
};
var getSize = function() {
return {
width: width,
height: height
};
};
return {
init: init,
update: update,
getRelPos: getRelPos,
getPos: getPos,
getSize: getSize
};
}());
var i = 0;
var space = 70;
for(i=0; i<400; i+=1) {
gameManager.addEntity(new Circle({
x: 100 + Math.random() * 4850,//(i%20) * space,
y: 100 + Math.random() * 4850,//Math.floor(i/20)*space,
color: ['red', 'blue', 'green', 'yellow', 'purple', 'brown', 'violet'][Math.floor(Math.random()*7)],
droplet: true
}));
}
gameManager.init('canvas1', 'canvas2', 'canvas3');
And last but not least, my css:
html, body {
background: gray;
width: 100%;
height: 100%;
margin: 0px;
}
#viewport {
position: relative;
width: 100%;
height: 100%;
}
#viewport canvas {
position: absolute;
}
canvas {
background-color: transparent;
}
I am trying to make a simple tank game based off of Nemesis, Isaac Sukin's game for AngelHack. Actually, what I'm trying to change is simple. I have a file named "Tank.obj" in a folder called "models".
I simple want to replace the cube with the enemy, with a model for a tank ("Tank.obj).
I keep on getting this error: Uncaught TypeError: vector.subSelf is not a function
I am not sure what this even means.....
Just so you know:
I have a fully updated Three.js file, and the OBJLoader.js is in my file hierarchy.
Here is the altered files (It's not altered that much, though):
/**
* Notes:
* - Coordinates are specified as (X, Y, Z) where X and Z are horizontal and Y
* is vertical
*/
var map = [ // 1 2 3 4 5 6 7 8 9
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1,], // 0
[1, 1, 0, 0, 0, 0, 0, 1, 1, 1,], // 1
[1, 1, 0, 0, 2, 0, 0, 0, 0, 1,], // 2
[1, 0, 0, 0, 0, 2, 0, 0, 0, 1,], // 3
[1, 0, 0, 2, 0, 0, 2, 0, 0, 1,], // 4
[1, 0, 0, 0, 2, 0, 0, 0, 1, 1,], // 5
[1, 1, 1, 0, 0, 0, 0, 1, 1, 1,], // 6
[1, 1, 1, 0, 0, 1, 0, 0, 1, 1,], // 7
[1, 1, 1, 1, 1, 1, 0, 0, 1, 1,], // 8
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1,], // 9
], mapW = map.length, mapH = map[0].length;
// Semi-constants
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight,
ASPECT = WIDTH / HEIGHT,
UNITSIZE = 250,
WALLHEIGHT = UNITSIZE / 3,
MOVESPEED = 100,
LOOKSPEED = 0.075,
BULLETMOVESPEED = MOVESPEED * 5,
NUMAI = 5,
PROJECTILEDAMAGE = 20;
// Global vars
var t = THREE, scene, cam, renderer, controls, clock, projector, model, skin;
var runAnim = true, mouse = { x: 0, y: 0 }, kills = 0, health = 100;
var healthCube, lastHealthPickup = 0;
/*
var finder = new PF.AStarFinder({ // Defaults to Manhattan heuristic
allowDiagonal: true,
}), grid = new PF.Grid(mapW, mapH, map);
*/
// Initialize and run on document ready
$(document).ready(function() {
$('body').append('<div id="intro">Click to start</div>');
$('#intro').css({width: WIDTH, height: HEIGHT}).one('click', function(e) {
e.preventDefault();
$(this).fadeOut();
init();
setInterval(drawRadar, 1000);
animate();
});
/*
new t.ColladaLoader().load('models/Yoshi/Yoshi.dae', function(collada) {
model = collada.scene;
skin = collada.skins[0];
model.scale.set(0.2, 0.2, 0.2);
model.position.set(0, 5, 0);
scene.add(model);
});
*/
});
// Setup
function init() {
clock = new t.Clock(); // Used in render() for controls.update()
projector = new t.Projector(); // Used in bullet projection
scene = new t.Scene(); // Holds all objects in the canvas
scene.fog = new t.FogExp2(0xD6F1FF, 0.0005); // color, density
// Set up camera
cam = new t.PerspectiveCamera(60, ASPECT, 1, 10000); // FOV, aspect, near, far
cam.position.y = UNITSIZE * .2;
scene.add(cam);
// Camera moves with mouse, flies around with WASD/arrow keys
controls = new t.FirstPersonControls(cam);
controls.movementSpeed = MOVESPEED;
controls.lookSpeed = LOOKSPEED;
controls.lookVertical = false; // Temporary solution; play on flat surfaces only
controls.noFly = true;
// World objects
setupScene();
// Artificial Intelligence
setupAI();
// Handle drawing as WebGL (faster than Canvas but less supported)
renderer = new t.WebGLRenderer();
renderer.setSize(WIDTH, HEIGHT);
// Add the canvas to the document
renderer.domElement.style.backgroundColor = '#D6F1FF'; // easier to see
document.body.appendChild(renderer.domElement);
// Track mouse position so we know where to shoot
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
// Shoot on click
$(document).click(function(e) {
e.preventDefault;
if (e.which === 1) { // Left click only
createBullet();
}
});
// Display HUD
$('body').append('<canvas id="radar" width="200" height="200"></canvas>');
$('body').append('<div id="hud"><p>Health: <span id="health">100</span><br />Score: <span id="score">0</span></p></div>');
$('body').append('<div id="credits"><p>Created by Isaac Sukin using Three.js<br />WASD to move, mouse to look, click to shoot</p></div>');
// Set up "hurt" flash
$('body').append('<div id="hurt"></div>');
$('#hurt').css({width: WIDTH, height: HEIGHT,});
}
// Helper function for browser frames
function animate() {
if (runAnim) {
requestAnimationFrame(animate);
}
render();
}
// Update and display
function render() {
var delta = clock.getDelta(), speed = delta * BULLETMOVESPEED;
var aispeed = delta * MOVESPEED;
controls.update(delta); // Move camera
// Rotate the health cube
healthcube.rotation.x += 0.004
healthcube.rotation.y += 0.008;
// Allow picking it up once per minute
if (Date.now() > lastHealthPickup + 60000) {
if (distance(cam.position.x, cam.position.z, healthcube.position.x, healthcube.position.z) < 15 && health != 100) {
health = Math.min(health + 50, 100);
$('#health').html(health);
lastHealthPickup = Date.now();
}
healthcube.material.wireframe = false;
}
else {
healthcube.material.wireframe = true;
}
// Update bullets. Walk backwards through the list so we can remove items.
for (var i = bullets.length-1; i >= 0; i--) {
var b = bullets[i], p = b.position, d = b.ray.direction;
if (checkWallCollision(p)) {
bullets.splice(i, 1);
scene.remove(b);
continue;
}
// Collide with AI
var hit = false;
for (var j = ai.length-1; j >= 0; j--) {
var a = ai[j];
var v = a.geometry.vertices[0];
var c = a.position;
var x = Math.abs(v.x), z = Math.abs(v.z);
//console.log(Math.round(p.x), Math.round(p.z), c.x, c.z, x, z);
if (p.x < c.x + x && p.x > c.x - x &&
p.z < c.z + z && p.z > c.z - z &&
b.owner != a) {
bullets.splice(i, 1);
scene.remove(b);
a.health -= PROJECTILEDAMAGE;
var color = a.material.color, percent = a.health / 100;
a.material.color.setRGB(
percent * color.r,
percent * color.g,
percent * color.b
);
hit = true;
break;
}
}
// Bullet hits player
if (distance(p.x, p.z, cam.position.x, cam.position.z) < 25 && b.owner != cam) {
$('#hurt').fadeIn(75);
health -= 10;
if (health < 0) health = 0;
val = health < 25 ? '<span style="color: darkRed">' + health + '</span>' : health;
$('#health').html(val);
bullets.splice(i, 1);
scene.remove(b);
$('#hurt').fadeOut(350);
}
if (!hit) {
b.translateX(speed * d.x);
//bullets[i].translateY(speed * bullets[i].direction.y);
b.translateZ(speed * d.z);
}
}
// Update AI.
for (var i = ai.length-1; i >= 0; i--) {
var a = ai[i];
if (a.health <= 0) {
ai.splice(i, 1);
scene.remove(a);
kills++;
$('#score').html(kills * 100);
addAI();
}
// Move AI
var r = Math.random();
if (r > 0.995) {
a.lastRandomX = Math.random() * 2 - 1;
a.lastRandomZ = Math.random() * 2 - 1;
}
a.translateX(aispeed * a.lastRandomX);
a.translateZ(aispeed * a.lastRandomZ);
var c = getMapSector(a.position);
if (c.x < 0 || c.x >= mapW || c.y < 0 || c.y >= mapH || checkWallCollision(a.position)) {
a.translateX(-2 * aispeed * a.lastRandomX);
a.translateZ(-2 * aispeed * a.lastRandomZ);
a.lastRandomX = Math.random() * 2 - 1;
a.lastRandomZ = Math.random() * 2 - 1;
}
if (c.x < -1 || c.x > mapW || c.z < -1 || c.z > mapH) {
ai.splice(i, 1);
scene.remove(a);
addAI();
}
/*
var c = getMapSector(a.position);
if (a.pathPos == a.path.length-1) {
console.log('finding new path for '+c.x+','+c.z);
a.pathPos = 1;
a.path = getAIpath(a);
}
var dest = a.path[a.pathPos], proportion = (c.z-dest[1])/(c.x-dest[0]);
a.translateX(aispeed * proportion);
a.translateZ(aispeed * 1-proportion);
console.log(c.x, c.z, dest[0], dest[1]);
if (c.x == dest[0] && c.z == dest[1]) {
console.log(c.x+','+c.z+' reached destination');
a.PathPos++;
}
*/
var cc = getMapSector(cam.position);
if (Date.now() > a.lastShot + 750 && distance(c.x, c.z, cc.x, cc.z) < 2) {
createBullet(a);
a.lastShot = Date.now();
}
}
renderer.render(scene, cam); // Repaint
// Death
if (health <= 0) {
runAnim = false;
$(renderer.domElement).fadeOut();
$('#radar, #hud, #credits').fadeOut();
$('#intro').fadeIn();
$('#intro').html('Ouch! Click to restart...');
$('#intro').one('click', function() {
location = location;
/*
$(renderer.domElement).fadeIn();
$('#radar, #hud, #credits').fadeIn();
$(this).fadeOut();
runAnim = true;
animate();
health = 100;
$('#health').html(health);
kills--;
if (kills <= 0) kills = 0;
$('#score').html(kills * 100);
cam.translateX(-cam.position.x);
cam.translateZ(-cam.position.z);
*/
});
}
}
// Set up the objects in the world
function setupScene() {
var UNITSIZE = 250, units = mapW;
// Geometry: floor
var floor = new t.Mesh(
new t.CubeGeometry(units * UNITSIZE, 10, units * UNITSIZE),
new t.MeshLambertMaterial({color: 0xEDCBA0,/*map: t.ImageUtils.loadTexture('images/floor-1.jpg')*/})
);
scene.add(floor);
// Geometry: walls
var cube = new t.CubeGeometry(UNITSIZE, WALLHEIGHT, UNITSIZE);
var materials = [
new t.MeshLambertMaterial({/*color: 0x00CCAA,*/map: t.ImageUtils.loadTexture('images/wall-1.jpg')}),
new t.MeshLambertMaterial({/*color: 0xC5EDA0,*/map: t.ImageUtils.loadTexture('images/wall-2.jpg')}),
new t.MeshLambertMaterial({color: 0xFBEBCD}),
];
for (var i = 0; i < mapW; i++) {
for (var j = 0, m = map[i].length; j < m; j++) {
if (map[i][j]) {
var wall = new t.Mesh(cube, materials[map[i][j]-1]);
wall.position.x = (i - units/2) * UNITSIZE;
wall.position.y = WALLHEIGHT/2;
wall.position.z = (j - units/2) * UNITSIZE;
scene.add(wall);
}
}
}
// Health cube
healthcube = new t.Mesh(
new t.CubeGeometry(30, 30, 30),
new t.MeshBasicMaterial({map: t.ImageUtils.loadTexture('images/health.png')})
);
healthcube.position.set(-UNITSIZE-15, 35, -UNITSIZE-15);
scene.add(healthcube);
// Lighting
var directionalLight1 = new t.DirectionalLight( 0xF7EFBE, 0.7 );
directionalLight1.position.set( 0.5, 1, 0.5 );
scene.add( directionalLight1 );
var directionalLight2 = new t.DirectionalLight( 0xF7EFBE, 0.5 );
directionalLight2.position.set( -0.5, -1, -0.5 );
scene.add( directionalLight2 );
}
var ai = [];
var aiGeo = new t.CubeGeometry(40, 40, 40);
function setupAI() {
for (var i = 0; i < NUMAI; i++) {
addAI();
}
}
function addAI() {
var c = getMapSector(cam.position);
var aiMaterial = new t.MeshBasicMaterial({/*color: 0xEE3333,*/map: t.ImageUtils.loadTexture('images/face.png')});
var object = new THREE.Mesh();
var loader = new THREE.OBJLoader();
loader.load( 'models/Tank.obj', function ( object ) {
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.material.map = aiMaterial;
}
} );
} );
do {
var x = getRandBetween(0, mapW-1);
var z = getRandBetween(0, mapH-1);
} while (map[x][z] > 0 || (x == c.x && z == c.z));
x = Math.floor(x - mapW/2) * UNITSIZE;
z = Math.floor(z - mapW/2) * UNITSIZE;
object.position.set(x, UNITSIZE * 0.15, z);
object.health = 100;
//o.path = getAIpath(o);
object.pathPos = 1;
object.lastRandomX = Math.random();
object.lastRandomZ = Math.random();
object.lastShot = Date.now(); // Higher-fidelity timers aren't a big deal here.
ai.push(object);
scene.add(object);
}
function getAIpath(a) {
var p = getMapSector(a.position);
do { // Cop-out
do {
var x = getRandBetween(0, mapW-1);
var z = getRandBetween(0, mapH-1);
} while (map[x][z] > 0 || distance(p.x, p.z, x, z) < 3);
var path = findAIpath(p.x, p.z, x, z);
} while (path.length == 0);
return path;
}
/**
* Find a path from one grid cell to another.
*
* #param sX
* Starting grid x-coordinate.
* #param sZ
* Starting grid z-coordinate.
* #param eX
* Ending grid x-coordinate.
* #param eZ
* Ending grid z-coordinate.
* #returns
* An array of coordinates including the start and end positions representing
* the path from the starting cell to the ending cell.
*/
function findAIpath(sX, sZ, eX, eZ) {
var backupGrid = grid.clone();
var path = finder.findPath(sX, sZ, eX, eZ, grid);
grid = backupGrid;
return path;
}
function distance(x1, y1, x2, y2) {
return Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
}
function getMapSector(v) {
var x = Math.floor((v.x + UNITSIZE / 2) / UNITSIZE + mapW/2);
var z = Math.floor((v.z + UNITSIZE / 2) / UNITSIZE + mapW/2);
return {x: x, z: z};
}
/**
* Check whether a Vector3 overlaps with a wall.
*
* #param v
* A THREE.Vector3 object representing a point in space.
* Passing cam.position is especially useful.
* #returns {Boolean}
* true if the vector is inside a wall; false otherwise.
*/
function checkWallCollision(v) {
var c = getMapSector(v);
return map[c.x][c.z] > 0;
}
// Radar
function drawRadar() {
var c = getMapSector(cam.position), context = document.getElementById('radar').getContext('2d');
context.font = '10px Helvetica';
for (var i = 0; i < mapW; i++) {
for (var j = 0, m = map[i].length; j < m; j++) {
var d = 0;
for (var k = 0, n = ai.length; k < n; k++) {
var e = getMapSector(ai[k].position);
if (i == e.x && j == e.z) {
d++;
}
}
if (i == c.x && j == c.z && d == 0) {
context.fillStyle = '#0000FF';
context.fillRect(i * 20, j * 20, (i+1)*20, (j+1)*20);
}
else if (i == c.x && j == c.z) {
context.fillStyle = '#AA33FF';
context.fillRect(i * 20, j * 20, (i+1)*20, (j+1)*20);
context.fillStyle = '#000000';
context.fillText(''+d, i*20+8, j*20+12);
}
else if (d > 0 && d < 10) {
context.fillStyle = '#FF0000';
context.fillRect(i * 20, j * 20, (i+1)*20, (j+1)*20);
context.fillStyle = '#000000';
context.fillText(''+d, i*20+8, j*20+12);
}
else if (map[i][j] > 0) {
context.fillStyle = '#666666';
context.fillRect(i * 20, j * 20, (i+1)*20, (j+1)*20);
}
else {
context.fillStyle = '#CCCCCC';
context.fillRect(i * 20, j * 20, (i+1)*20, (j+1)*20);
}
}
}
}
var bullets = [];
var sphereMaterial = new t.MeshBasicMaterial({color: 0x333333});
var sphereGeo = new t.SphereGeometry(2, 6, 6);
function createBullet(obj) {
if (obj === undefined) {
obj = cam;
}
var sphere = new t.Mesh(sphereGeo, sphereMaterial);
sphere.position.set(obj.position.x, obj.position.y * 0.8, obj.position.z);
if (obj instanceof t.Camera) {
var vector = new t.Vector3(mouse.x, mouse.y, 1);
projector.unprojectVector(vector, obj);
sphere.ray = new t.Ray(
obj.position,
vector.subSelf(obj.position).normalize()
);
}
else {
var vector = cam.position.clone();
sphere.ray = new t.Ray(
obj.position,
vector.subSelf(obj.position).normalize()
);
}
sphere.owner = obj;
bullets.push(sphere);
scene.add(sphere);
return sphere;
}
/*
function loadImage(path) {
var image = document.createElement('img');
var texture = new t.Texture(image, t.UVMapping);
image.onload = function() { texture.needsUpdate = true; };
image.src = path;
return texture;
}
*/
function onDocumentMouseMove(e) {
e.preventDefault();
mouse.x = (e.clientX / WIDTH) * 2 - 1;
mouse.y = - (e.clientY / HEIGHT) * 2 + 1;
}
// Handle window resizing
$(window).resize(function() {
WIDTH = window.innerWidth;
HEIGHT = window.innerHeight;
ASPECT = WIDTH / HEIGHT;
if (cam) {
cam.aspect = ASPECT;
cam.updateProjectionMatrix();
}
if (renderer) {
renderer.setSize(WIDTH, HEIGHT);
}
$('#intro, #hurt').css({width: WIDTH, height: HEIGHT,});
});
// Stop moving around when the window is unfocused (keeps my sanity!)
$(window).focus(function() {
if (controls) controls.freeze = false;
});
$(window).blur(function() {
if (controls) controls.freeze = true;
});
//Get a random integer between lo and hi, inclusive.
//Assumes lo and hi are integers and lo is lower than hi.
function getRandBetween(lo, hi) {
return parseInt(Math.floor(Math.random()*(hi-lo+1))+lo, 10);
}
<!DOCTYPE html>
<html>
<head>
<title>WebGL FPS - AngelHack SV 2012 - Isaac Sukin</title>
<style>
body {
background-image: url('images/screenshot.jpg');
cursor: crosshair;
font-family: Georgia, Helvetica, sans-serif;
font-weight: bold;
margin: 0;
overflow: hidden;
padding: 0;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
#hud {
bottom: 10px;
position: absolute;
right: 10px;
z-index: 100;
}
#credits {
font-size: 12px;
position: absolute;
text-align: center;
top: 10px;
width: 100%;
z-index: 100;
}
p {
background-color: #EEEEEE;
border: 1px solid black;
display: inline-block;
margin: 0;
padding: 5px;
}
#radar {
background-color: #EEEEEE;
border: 1px solid black;
bottom: 10px;
left: 10px;
position: absolute;
z-index: 100;
}
#intro {
background-color: #000000;
color: #FFFFFF;
cursor: pointer;
font-size: 32px;
opacity: 0.7;
padding: 120px;
text-align: center;
z-index: 1001;
}
#hurt {
background-color: red;
display: none;
left: 0;
opacity: 0.15;
pointer-events: none;
position: absolute;
top: 0;
z-index: 1002;
}
</style>
<script src="jquery-1.7.2.min.js"></script>
<!-- <script src="PointerLock.js"></script> --> <!-- Insufficient browser support, interferes with other mouse events -->
<!-- <script src="Pathfinder.js"></script> -->
<script src="Three.js"></script>
<script src="OBJLoader.js"></script>
<!-- <script src="ColladaLoader.js"></script> -->
<script src="Three.FirstPersonControls.js"></script>
<script src="main.js"></script>
</head>
<body>
</body>
</html>
I hope anyone out there can help!
Thanks,
Noah
In Three.js the subSelf function was renamed to sub(), so you need to update the code to be:
sphere.ray = new t.Ray(
obj.position,
vector.sub(obj.position).normalize()
);