I am building a tower defence game and I am facing an issue in Phaser with tiled maps.
You see different layers in tiled always have coordinates from (0,0) to (600,600) -> or whatever your tiled width and height is. I have a backgroundLayer and a path. The creeps properly collide with the path.
Now I am trying to change the cursor to a building sprite (for UI when I click on some button and select a tower I want it to turn red when it is on the road or above another tower/creep) and I am using an overlap function for path and buildings.
This overlap function in Phaser checks the rectangle of building sprite and path, but path rectangle is the whole map. The collision works but I only need an overlap. Any ideas how to achieve this? Here is my code.
var RedPlanetGame = RedPlanetGame || {};
var pressed = {
is: false
};
var buildingOverlaps = {
is: false
};
//title screen
RedPlanetGame.Game = function () {
};
RedPlanetGame.Game.prototype = {
create: function create() {
//A door for multyplayer
this.players = [];
this.player = new Player(1, 'Daniel', 300);
this.players.push(this.player);
this.playerInfo = {};
//Tile map
this.map = this.game.add.tilemap('sample2');
this.map.addTilesetImage('32x32_map_tile v3.1 [MARGINLESS]', 'gameTiles');
//background and layers
this.backgroundlayer = this.map.createLayer('backgroundLayer');
this.path = this.map.createLayer('path');
this.map.setCollisionBetween(1, 2000, true, 'backgroundLayer');
this.map.setCollisionBetween(1, 2000, true, 'path');
//objects from tile map
this.spawnCreepsAt = this.map.objects['objectsLayer'][0];
this.destinationForCreeps = this.map.objects['objectsLayer'][1];
//resize world
this.backgroundlayer.resizeWorld();
//this.game.world.setBounds(0, 0, 100, 100);
//groups
this.game.enemies = new UnitsPoolFactory(this.game);
this.game.buildings = this.game.add.group();//TODO: make buildings for each player
this.game.bullets = new BulletsPoolFactory(this.game);
//creep spawning
var _this = this;
const creepYOffset = 15;
setInterval(function () {
_this.game.enemies.factory(_this.spawnCreepsAt.x, _this.spawnCreepsAt.y + creepYOffset, UNIT_TYPES.CREEP1);
}, 1000);
//text and player info
var textX = 150;
var textY = 0;
this.playerInfo.gold = this.game.add.text(textX, textY, 'Player gold: ' + this.player.gold,
{font: "24px Arial", fill: '#FFD700'}
);
//Here is test straight forward code for building towers
this.game.build = this.game.add.group();
this.buildingSprite = this.game.add.sprite(0, 0, 'tower1-1');
this.game.physics.enable(this.buildingSprite, Phaser.Physics.ARCADE);
this.buildingSprite.anchor.setTo(0.5);
this.buildingSprite.scale.setTo(0.5);
this.game.build.add(this.buildingSprite);
console.log(this.path.getBounds());
console.log(this.backgroundlayer.getBounds());
},
update: function update() {
var _this = this;
//Camera follow cursor
if (this.game.input.mousePointer.x > gameHeight - gameHeight / 10) {
this.game.camera.x += 10;
} else if (this.game.input.mousePointer.x <= 100) {
this.game.camera.x -= 10;
}
if (this.game.input.mousePointer.y > gameWidth - gameWidth / 10) {
this.game.camera.y += 10;
} else if (this.game.input.mousePointer.y <= 100) {
this.game.camera.y -= 10;
}
//check for collision between enemy and non-path layer
this.game.physics.arcade.collide(this.game.enemies, this.backgroundlayer);
//checks for collision between bullets and enemies
this.game.physics.arcade.overlap(this.game.bullets, this.game.enemies, function (bullet, enemy) {
enemy.takeHit(bullet, _this.player);
bullet.kill();
}, null, this);
//updates enemies
this.game.enemies.forEach(function (enemy) {
enemy.onUpdate(_this.destinationForCreeps);
});
//updates buildings
this.game.buildings.forEach(function (building) {
building.onUpdate(_this.game.bullets);
});
//on mouse down event
if (this.game.input.activePointer.leftButton.isDown && !pressed.is) {//yo Yoda
//builds new building of type TOWER1
buffer(pressed, 1000);
if (Building.prototype.canBuild(this.player.gold, Tower1.prototype.moneyCost)) {
var poss = this.game.input.mousePointer;
BuildingsFactory(this.game, poss.x, poss.y, this.player, BUILDING_TYPES.TOWER1);
this.player.gold -= Tower1.prototype.moneyCost
} else {
alert('Not enought gold');
}
}
//Here is test straight forward code for building towers
if (Phaser.Rectangle.contains(this.buildingSprite.body, this.game.input.x, this.game.input.y)) {
this.buildingSprite.body.velocity.setTo(0, 0);
}
else {
this.game.physics.arcade.moveToPointer(this.buildingSprite, 700);
}
this.game.physics.arcade.collideGroupVsTilemapLayer(this.game.build, this.path, function (sprite) {
if (!buildingOverlaps.is) {
buffer(buildingOverlaps, 500);
console.log('overlap')
}
}, null, this, true);
},
render: function render() {
this.playerInfo.gold.text = 'Player gold: ' + this.player.gold;
}
};
Related
I am trying to port the following Phaser2 example (https://phaser.io/examples/v2/tilemaps/fill-tiles) into Phaser3. I am experiencing two issues:
I cannot rotate the vehicle as the left/right keys will not work.
I cannot get the tilemap to display correctly, it seems cut off.
import Phaser from "phaser";
const config = {
type: Phaser.AUTO,
parent: "phaser-example",
width: 800,
height: 600,
physics: {
default: 'arcade'
},
scene: {
preload: preload,
create: create,
update: update,
render: render
}
};
const game = new Phaser.Game(config);
let cursors;
let player;
let map;
let speed = 0;
function preload() {
this.load.tilemapTiledJSON('desert', 'desert.json');
this.load.image('tiles', 'https://examples.phaser.io/assets/tilemaps/tiles/tmw_desert_spacing.png')
this.load.image('car', 'http://labs.phaser.io/assets/sprites/car90.png')
}
function create() {
map = this.make.tilemap({ key: 'desert' });
const tiles = map.addTilesetImage('Desert', 'tiles');
const layer = map.createDynamicLayer('Ground', tiles, 0, 0);
cursors = this.input.keyboard.createCursorKeys();
player = this.physics.add.sprite(450, 80, 'car');
this.cameras.main.startFollow(player, true, 0.05, 0.05);
}
function update() {
// Drive forward if cursor up key is pressed down
if (cursors.up.isDown && speed <= 400) {
speed += 10;
} else {
if (speed >= 10) {
speed -= 10
}
}
// Drive backwards if cursor down key is pressed down
if (cursors.down.isDown && speed >= -200) {
speed -= 5;
} else {
if (speed <= -5) {
speed += 5
}
}
// Steer the car
if (cursors.left.isDown) {
player.body.angularVelocity = -5 * (speed / 1000);
} else if (cursors.right.isDown) {
player.body.angularVelocity = 5 * (speed / 1000);
} else {
player.body.angularVelocity = 0;
}
player.body.velocity.x = speed * Math.cos((player.body.angle - 360) * 0.01745);
player.body.velocity.y = speed * Math.sin((player.body.angle - 360) * 0.01745);
}
function render() {
}
How can I fix this?
I loaded the code in a blank project without assets, and was able to see the image missing object rotate. However, it looks like the dependency of speed in angular rotation is too low. If you reduce the decrease in scaling of speed used for calculating angular velocity, you can get the car to turn more quickly.
Proof -> https://stackblitz.com/edit/phaser-2-example-so
Can someone explain where is my mistake in trying to make my player to collide with the bound of the world not just going throw the walls. I tried with custom methods but it is not the effect that I desire.
When I add "game.world.setBounds(0, 0, x, y);" to the create function my game does not start. I am begginer in phaser js so maybe I am doing something wrong. Here is my code:
"use strict";
var game = new Phaser.Game(1000, 800, Phaser.CANVAS, "game_div");
var spaceField,
backgroundSpeed,
player,
cursors,
bullets,
bulletsTime = 0,
fireButton,
bullet,
bulletSound;
var mainState = {
preload: function () {
//id
game.load.image("starfield", "images/space.png");
game.load.image("player", "images/playerSmall.png");
game.load.image("bullet", "images/fire.png");
// audio
game.load.audio("bulletSound", "sounds/blaster.mp3");
},
create: function () {
// Full screen when clicking with the mouse on the screen
game.scale.fullScreenScaleMode = Phaser.ScaleManager.EXACT_FIT;
game.input.onDown.add(goFull, this);
// background
spaceField = game.add.tileSprite(0, 0, 1000, 800, "starfield");
backgroundSpeed = 2;
game.physics.setBoundsToWorld();
// player spaceship + adding physics + player movement
player = game.add.sprite(game.world.centerX, game.world.centerY + 300, "player");
game.physics.enable(player, Phaser.Physics.ARCADE);
cursors = game.input.keyboard.createCursorKeys();
// Fire bullets
bullets = game.add.group();
bullets.enableBody = true;
bullets.physicsBodyType = Phaser.Physics.ARCADE; // Enabling physics for bullets
bullets.createMultiple(30, "bullet");
bullets.setAll("anchor.x", 0.5);
bullets.setAll("anchor.y", 1);
bullets.setAll("outOfBoundsKill", true); // Checks if the bullet is off screen so we can reuse it
bullets.setAll("checkWorldBounds", true);
fireButton = game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);
bulletSound = game.add.audio("bulletSound");
},
update: function () {
// Making scrolling background
spaceField.tilePosition.y += backgroundSpeed;
player.body.velocity.x = 0; // Everytime when key is not pressed the player does not move
player.body.velocity.y = 0;
// Checking which key is pressed
if (cursors.up.isDown) {
player.checkWorldBounds = true;
player.events.onOutOfBounds.add(playerOutOfBoundsTop, this);
player.body.velocity.y = -350;
}
if (cursors.down.isDown) {
player.checkWorldBounds = true;
// player.events.onOutOfBounds.add(playerOutOfBoundsBottom, this);
player.body.velocity.y = 350;
}
if (cursors.left.isDown) {
player.body.velocity.x = -350;
}
if (cursors.right.isDown) {
player.body.velocity.x = 350;
}
if (fireButton.isDown) {
fireBullet();
}
}
};
function fireBullet() {
if (game.time.now > bulletsTime) {
bullet = bullets.getFirstExists(false);
if (bullet) {
bullet.reset(player.x + 28, player.y);
bullet.bulletAngleOffset = 90;
bullet.bulletAngleVariance = 30;
bullet.body.velocity.y = -400;
bulletsTime = game.time.now + 200;
bulletSound.play();
}
}
}
function playerOutOfBoundsTop(player) {
// Move the Spaceship to the top of the screen again
player.reset(player.x, 60);
}
/*function playerOutOfBoundsBottom(player) {
// Move the spaceship to the bottom of the screen again
player.reset(60, player.x);
}
*/
function goFull() {
if (game.scale.isFullScreen) {
game.scale.stopFullScreen();
} else {
game.scale.startFullScreen(false);
}
}
//id
game.state.add('mainState', mainState);
game.state.start("mainState");
If you want that your player doesn't go out of the game bounds you can just set the property collideWorldBounds to true, in the create function:
player.body.collideWorldBounds=true;
Take a look a this phaser example and the docs
But if you want to do something different when the player is out of bounds you can add a function (like the one you have when the UP key is pressed) in the update loop.
For example, with this code when your player is out of bounds it will appear again in the middle of the game:
var game = new Phaser.Game(1000, 800, Phaser.CANVAS, "game_div");
var spaceField,
backgroundSpeed,
player,
cursors,
bullets,
bulletsTime = 0,
fireButton,
bullet,
bulletSound;
var mainState = {
preload: function () {
//id
game.load.image("starfield", "images/space.png");
game.load.image("player", "images/playerSmall.png");
game.load.image("bullet", "images/fire.png");
// audio
game.load.audio("bulletSound", "sounds/blaster.mp3");
},
create: function () {
// Full screen when clicking with the mouse on the screen
game.scale.fullScreenScaleMode = Phaser.ScaleManager.EXACT_FIT;
game.input.onDown.add(goFull, this);
// background
spaceField = game.add.tileSprite(0, 0, 1000, 800, "starfield");
backgroundSpeed = 2;
game.physics.setBoundsToWorld();
// player spaceship + adding physics + player movement
player = game.add.sprite(game.world.centerX, game.world.centerY + 300, "player");
game.physics.enable(player, Phaser.Physics.ARCADE);
cursors = game.input.keyboard.createCursorKeys();
//player.body.collideWorldBounds=true;
// Fire bullets
bullets = game.add.group();
bullets.enableBody = true;
bullets.physicsBodyType = Phaser.Physics.ARCADE; // Enabling physics for bullets
bullets.createMultiple(30, "bullet");
bullets.setAll("anchor.x", 0.5);
bullets.setAll("anchor.y", 1);
bullets.setAll("outOfBoundsKill", true); // Checks if the bullet is off screen so we can reuse it
bullets.setAll("checkWorldBounds", true);
fireButton = game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);
bulletSound = game.add.audio("bulletSound");
},
update: function () {
// Making scrolling background
spaceField.tilePosition.y += backgroundSpeed;
player.body.velocity.x = 0; // Everytime when key is not pressed the player does not move
player.body.velocity.y = 0;
player.events.onOutOfBounds.add(playerOutOfBounds, this);
// Checking which key is pressed
if (cursors.up.isDown) {
player.checkWorldBounds = true;
player.body.velocity.y = -350;
}
if (cursors.down.isDown) {
player.checkWorldBounds = true;
// player.events.onOutOfBounds.add(playerOutOfBoundsBottom, this);
player.body.velocity.y = 350;
}
if (cursors.left.isDown) {
player.body.velocity.x = -350;
}
if (cursors.right.isDown) {
player.body.velocity.x = 350;
}
if (fireButton.isDown) {
fireBullet();
}
}
};
function fireBullet() {
if (game.time.now > bulletsTime) {
bullet = bullets.getFirstExists(false);
if (bullet) {
bullet.reset(player.x + 28, player.y);
bullet.bulletAngleOffset = 90;
bullet.bulletAngleVariance = 30;
bullet.body.velocity.y = -400;
bulletsTime = game.time.now + 200;
bulletSound.play();
}
}
}
function playerOutOfBounds(player) {
// Move the Spaceship to the top of the screen again
player.reset(player.x, game.world.centerX);
player.reset(player.y, game.world.centerY);
}
/*function playerOutOfBoundsBottom(player) {
// Move the spaceship to the bottom of the screen again
player.reset(60, player.x);
}
*/
function goFull() {
if (game.scale.isFullScreen) {
game.scale.stopFullScreen();
} else {
game.scale.startFullScreen(false);
}
}
//id
game.state.add('mainState', mainState);
game.state.start("mainState");
i have been having trouble with reading a mouse position on a canvas. The code is working (semi) correctly as it reads the position when clicking he canvas in IE but only on one frame, in chrome it is just displaying the value as 0.
Here is the full code:
<script>
var blip = new Audio("blip.mp3");
blip.load();
var levelUp = new Audio("levelUp.mp3");
levelUp.load();
var canvas = document.getElementById('game');
var context = canvas.getContext('2d');
context.font = '18pt Calibri';
context.fillStyle = 'white';
//load and draw background image
var bgReady = false;
var background = new Image();
background.src = 'images/background.jpg';
background.onload = function(){
bgReady = true;
}
var startMessage = 'Click the canvas to start';
//load plane image
var planeReady = false;
var planeImage = new Image();
planeImage.src = 'images/plane.png';
planeImage.onload = function() {
planeReady = true;
}
//load missile image
var missileReady = false;
var missileImage = new Image();
missileImage.src = 'images/missile-flipped.gif';
missileImage.onload = function() {
missileReady = true;
}
//initialise lives and score
var score = 0;
var lives = 3;
var missilesLaunched = 0;
var missileSpeed = 5;
var level = 1;
var missileX = 960;
var missileY = Math.random() * 500;
if (missileY > 480) {
missileY = 480;
}
function getMousePos(canvas, event) {
return {
x: input.x - rect.left,
y: input.y - rect.top
};
}
function update_images(event) {
var pos = getMousePos(canvas.getBoundingClientRect(), mouseInput);
planeImage.y = pos.y;
missileX = missileX - missileSpeed;
if (missileX < - 70) {
missilesLaunched++;
missileX = 960;
missileY = Math.random() * 500;
if (missileY > 480) {
missileY = 480;
}
blip.play();
score = missilesLaunched;
if (score % 5 == 0) {
missileSpeed = missileSpeed + 2;
level++;
levelUp.play();
}
}
}
function reload_images() {
if (bgReady = true) {
context.drawImage(background, 0, 0);
}
if (planeReady = true) {
context.drawImage(planeImage, 10, planeImage.y);
}
if (missileReady = true) {
context.drawImage(missileImage, missileX, missileY);
}
context.fillText('Lives: ' + lives, 200, 30);
context.fillText('Score: ' + score, 650, 30);
context.fillText('Level: ' + missileSpeed, 420, 30);
context.fillText('Position: ' + missileImage.y, 420, 70);
}
function main(event) {
var mouseInput = { x: 0, y: 0 };
document.addEventListener("mousemove", function (event) {
mouseInput.x = event.clientX;
mouseInput.y = event.clientY;
});
update_images(event);
reload_images();
if (lives > 0) {
window.requestAnimationFrame(main);
}
else {
}
}
function start() {
context.drawImage(background, 0, 0);
context.fillText('Click the canvas to start', 350, 250);
function startMain(event) {
game.removeEventListener("click", startMain);
main(event);
}
canvas.addEventListener("mousedown", startMain);
}
start();
</script>
Joe, you should actually be capturing the mouse position every time you click...
...but you're actually also starting a new game (without stopping the old one), every time you click, too.
First problem: starting game engine several times to draw on the same instance of the canvas
Solution:
In your start function, you need to remove the mousedown event listener, after you've triggered it.
function start () {
// ... other setup
function startMain (event) {
canvas.removeEventListener("click", startMain);
main(event);
}
canvas.addEventListener("click", startMain);
}
Now it will only listen for the first click, before starting, and will only start once.
Second Problem: mouse doesn't update as expected
Solution: two issues here...
...first, you are passing event into main on first call...
...after that, you're passing main into requestAnimationFrame.
requestAnimationFrame won't call it with an event, it will call it with the number of microseconds (or ms or some other unit as a fractional precision of ms) since the page was loaded.
So the first time you got main({ type: "mousedown", ... });.
The next time you get main(4378.002358007);
So lets refactor the startMain we had above, so that main never ever collects an event, just a time.
function startMain ( ) {
canvas.removeEventListener("click", startMain);
requestAnimationFrame(main);
}
The next problem is that even if you were getting just events, you're only ever capturing a click event (which as we mentioned earlier, fires a new copy of the game logic).
Your solution is to separate the code which catches mouse events from the code which reads mouse position.
var mouseInput = { x: 0, y: 0 };
document.addEventListener("mousemove", function (event) {
mouseInput.x = event.clientX;
mouseInput.y = event.clientY;
});
function getMousePos (rect, input) {
return {
x : input.x - rect.left,
y : input.y - rect.top
};
}
// currently in updateImages (should not be there, but... a different story)
var pos = getMousePos(canvas.getBoundingClientRect(), mouseInput);
You've got other problems, too...
You're calling getMousePos and passing in game at the moment. I don't see where game is defined in your JS, so either you're making game somewhere else (begging for bugs), or it's undefined, and your app blows up right there.
You should really be building this with your console / dev-tools open, in a hands-on fashion, and cleaning bugs in each section, as you go.
I am making a top-down racing game using the Phaser framework which uses JS. I am having some trouble getting the car to slow down, at the moment it just stops when no button is pressed. I want it to slow down to stopped. Here is my code so far:
create: function () {
//run in canvas mode
this.game.renderer.clearBeforeRender - false;
this.game.renderer.roundPixels = true;
//Add arcade physics
this.game.physics.startSystem(Phaser.Physics.ARCADE);
//Add background sprites
this.game.add.sprite(0, 0, 'background');
this.game.add.sprite(0, 0, 'track');
//Add car sprite
car = this.game.add.sprite(800, 135, 'car-concept');
//Car physics settings
this.game.physics.enable(car, Phaser.Physics.ARCADE);
car.body.drag.set(100);
car.body.maxVelocity.set(200);
car.body.maxAngular = 500;
car.body.angularDrag = 500;
car.body.collideWorldBounds = true;
//Game input
cursors = this.game.input.keyboard.createCursorKeys();
this.game.input.keyboard.addKeyCapture([ Phaser.Keyboard.SPACEBAR]);
//pivot point
car.pivot.x = car.width * .3;
car.pivot.y = car.height * .5;
car.anchor.setTo(0.3, 0.5);
//scale the car
car.scale.setTo(0.3, 0.3);
},
update: function () {
car.body.velocity.x = 0;
car.body.velocity.y = 0;
car.body.angularVelocity = 0;
if(cursors.left.isDown)
{
car.body.angularVelocity = -200;
}
else if(cursors.right.isDown)
{
car.body.angularVelocity = 200;
}
if(cursors.up.isDown)
{
this.game.physics.arcade.velocityFromAngle(car.angle, -200, car.body.velocity)
if(this.currentSpeed < this.maxSpeed)
{
this.currentSpeed += 10;
}
}
else
{
if(this.currentSpeed > 0)
{
this.currentSpeed -= 10;
}
}
if(cursors.down.isDown)
{
this.game.physics.arcade.velocityFromAngle(car.angle, 200, car.body.velocity)
if(this.currentSpeed > 0)
{
this.currentSpeed -= 30;
}
}
},
speed: function()
{
this.maxSpeed = 100;
this.currentSpeed = 0;
},
I have looked a few questions on here about the same problem and this is how I got to the point where I am now. I have set the maxSpeed and currentSpeed but for some reason it won't allow me to actually use it. The console in the browser does not give me any errors, so if anyone can guide me on this, that would be great!
Thanks.
The reason it stops dead is because you're moving it with velocity, not acceleration - and are setting velocity to zero in your update loop (this effectively says "stop, now!").
Remove those lines and in your call to velocityFromAngle you should feed that into body.acceleration instead of body.velocity.
Here is an update loop you can use, although you'll need to mix in your currentSpeed settings and such like:
function update() {
if (cursors.up.isDown)
{
game.physics.arcade.accelerationFromRotation(sprite.rotation, 200, sprite.body.acceleration);
}
else
{
sprite.body.acceleration.set(0);
}
if (cursors.left.isDown)
{
sprite.body.angularVelocity = -300;
}
else if (cursors.right.isDown)
{
sprite.body.angularVelocity = 300;
}
else
{
sprite.body.angularVelocity = 0;
}
}
you never used currentSpeed. You set your speed to -200 and 200 in velocityFromAngle.
I have a snap svg animation which animates a bunch of circles, and draws a line between them if they are within a certain proximity of each other. However, I realize that there is a lot of optimizing I can do, but I'm not exactly sure how to do it. I feel like it would be useful to
have a good example of proximity detection in snap
have some more information on optimizing animations in snap svg. It hasn't been easy to find.
Here is a working example of the animation:
http://jsfiddle.net/heaversm/sbj4W/1/
and here are the things I believe can be optimized:
Each circle calls its own animation function - the circles have all been added to a group, and I'm guessing there is a way to apply random motion to all members of a group that is more performant than call a function for each and every element within the group.
for (var i=0; i<this.drawingConfig.circles.amount;i++){
...
this.animateSingle(circleShape);
}
The proximity function is awkward - for each circle, for each update cycle, I have to loop through an array of all the other circles and find out if the X and Y coordinates are close enough to draw a line to. Plus, that means you're getting duplicate lines, because each circle will draw a line to its neighbors, instead of having a single shared line between the two.
for (var i=0;i<circles.length;i++){
var nextCircle = circles[i].node;
var nextCircleX = nextCircle.cx.baseVal.value;
var distance = Math.abs(nextCircleX-thisCircleX);
var proximity = mainModule.drawingConfig.circles.proximity;
if (distance < proximity){
var nextCircleY = nextCircle.cy.baseVal.value;
var thisCircleY = shape.node.cy.baseVal.value;
var distanceY = Math.abs(nextCircleY - thisCircleY);
if (distanceY < proximity){
var line = mainModule.s.line(thisCircleX, thisCircleY, nextCircleX, nextCircleY).attr({stroke: '#a6a8ab', strokeWidth: '1px'});
mainModule.drawingConfig.circles.circleGroup.add(line);
}
}
}
Correspondingly, I each circle's animation function clears all the lines on the screen. Ideally all the circles would be sharing one update function, and in that function, you'd clear the lines.
Snap.animate(startX, animX, function (val) {
var lines = Snap.selectAll('line');
lines.remove();
...
}, mainModule.drawingConfig.circles.animTime);
Right now, I can tell the renderer can't keep up with all of the various animations / loops. Any help optimizing the above things (or anything else you can see that I'm doing weird, would be greatly appreciated.
I cleaned this up by running only one animation loop, on a timer every 10ms, and animated the position of the circles by just giving them a slope and, each update, continuing them further along that slope. You can see an updated fiddle here:
http://jsfiddle.net/heaversm/fJ6fj/
var mainModule = {
s: Snap("#svg"),
drawingConfig: {
circles: {
amount: 20,
sizeMin: 10,
sizeMax: 20,
proximity: 100,
circleGroup: null,
circleArray: [],
animTime: 2000
},
canvas: {
width: 800,
height: 600
}
},
init: function(){
//this.sizeCanvas();
this.makeCircles();
},
sizeCanvas: function(){
$('#svg').width(800).height(600);
},
makeCircles: function(){
this.drawingConfig.circles.circleGroup = this.s.g();
for (var i=0; i<this.drawingConfig.circles.amount;i++){
var circleX = this.randomNumber(0, this.drawingConfig.canvas.width);
var circleY = this.randomNumber(0, this.drawingConfig.canvas.height);
var circleRadius = this.randomNumber(this.drawingConfig.circles.sizeMin,this.drawingConfig.circles.sizeMax);
var circleFill = '#'+Math.floor(Math.random()*16777215).toString(16);
var circleShape = this.s.circle(circleX, circleY, circleRadius);
circleShape.attr({
fill: circleFill
});
this.drawingConfig.circles.circleGroup.add(circleShape);
var circleIncline = this.setIncline();
var circleObj = { incline: circleIncline, shape: circleShape };
this.drawingConfig.circles.circleArray.push(circleObj);
}
this.update();
},
setIncline: function(){
return { incX: this.randomNumber(-5,5), incY: this.randomNumber(-5,5) }
},
update: function(){
var lines = Snap.selectAll('line');
lines.remove();
for (var i=0; i<this.drawingConfig.circles.amount; i++){
var circle = this.drawingConfig.circles.circleArray[i];
var circleX = circle.shape.node.cx.animVal.value;
var circleY = circle.shape.node.cy.animVal.value;
this.move(circle,circleX,circleY);
for (var j=0;j<i;j++){
if (i != j){
var circle2 = this.drawingConfig.circles.circleArray[j];
var circle2X = circle2.shape.node.cx.animVal.value;
var circle2Y = circle2.shape.node.cy.animVal.value;
var dist = mainModule.distance(circleX,circleY,circle2X,circle2Y);
if (dist <= mainModule.drawingConfig.circles.proximity){ //
var lineWeight = 10/dist;
var line = mainModule.s.line(circleX, circleY, circle2X, circle2Y).attr({stroke: '#a6a8ab', strokeWidth: '1px'});
}
if (dist <= 10) { //collision
circle.incline = mainModule.setIncline();
circle2.incline = mainModule.setIncline();
}
}
}
}
setTimeout(function(){ mainModule.update(); },10);
},
distance: function(circleX,circleY,circle2X,circle2Y){
var distX = circle2X - circleX;
var distY = circle2Y - circleY;
distX = distX*distX;
distY = distY*distY;
return Math.sqrt(distX + distY);
},
move: function(circle,curX,curY){
if (curX > this.drawingConfig.canvas.width || curX < 0) {
circle.incline.incX = -circle.incline.incX;
}
if (curY > this.drawingConfig.canvas.height || curY < 0) {
circle.incline.incY = -circle.incline.incY;
}
curX = curX + circle.incline.incX;
curY = curY + circle.incline.incY;
if (curX > this.drawingConfig.canvas.width) {
curX = this.drawingConfig.canvas.width;
circle.incline = this.setIncline();
} else if (curX < 0) {
curX = 0;
circle.incline = this.setIncline();
}
if (curY > this.drawingConfig.canvas.height) {
curY = this.drawingConfig.canvas.height;
circle.incline = this.setIncline();
} else if (curY < 0) {
curY = 0;
circle.incline = this.setIncline();
}
circle.shape.attr({ cx: curX, cy: curY });
},
randomNumber: function(min,max){
return Math.floor(Math.random()*(max-min+1)+min);
},
getBounds: function(shape){
shapeBox = shape.node.getBoundingClientRect();
}
}
mainModule.init();