new guy to stack and to java.
to start off, here's my code,
AsteroidsGame.java
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.net.*;
import java.util.*;
public class AsteroidsGame extends JApplet implements Runnable, KeyListener {
Thread thread;
Dimension dim;
Image img;
Graphics g;
long endTime, startTime, framePeriod;
Ship ship;
boolean paused;
// True if the game is paused. Enter is the pause key
Shot[] shots;
int numShots;
boolean shooting;
Asteroid[] asteroids;
int numAsteroids;
double astRadius;
double minAstVel;
double maxAstVel;
int astNumHits;
int astNumSplit;
int level;
private AudioClip audioClip;
public void init() {
resize(500, 500);
shots = new Shot[41];
numAsteroids = 0;
level = 0;
astRadius = 60;
minAstVel = .5;
maxAstVel = 5;
astNumHits = 3;
astNumSplit = 2;
endTime = 0;
startTime = 0;
framePeriod = 25;
addKeyListener(this);
setFocusable(true);
requestFocusInWindow();
dim = getSize();
img = createImage(dim.width, dim.height);
g = img.getGraphics();
thread = new Thread(this);
thread.start();
URL urlAudioClip = getClass().getResource("audio/minorcircuit.wav");
audioClip = Applet.newAudioClip(urlAudioClip);
audioClip.loop();
}
public void start() {
audioClip.loop();
}
public void stop() {
audioClip.stop();
}
public void setUpNextLevel() {
level++;
ship = new Ship(250, 250, 0, .35, .98, .1, 12);
numShots = 0;
paused = false;
shooting = false;
asteroids = new Asteroid[level * (int)Math.pow(astNumSplit, astNumHits - 1) + 1];
numAsteroids = level;
for(int i = 0; i < numAsteroids; i++) {
asteroids[i] = new Asteroid(Math.random() * dim.width, Math.random() * dim.height, astRadius, minAstVel, maxAstVel, astNumHits, astNumSplit);
}
}
public void paint(Graphics gfx) {
g.setColor(Color.black);
g.fillRect(0, 0, 500, 500);
for(int i = 0; i < numShots; i++) {
shots[i].draw(g);
}
for(int i = 0; i < numAsteroids; i++) {
asteroids[i].draw(g);
}
ship.draw(g);
//draw the ship
g.setColor(Color.cyan);
g.drawString("Level " + level, 20, 20);
gfx.drawImage(img, 0, 0, this);
}
public void update(Graphics gfx) {
paint(gfx);
}
public void run() {
for(;;) {
startTime = System.currentTimeMillis();
//start next level when all asteroids are destroyed
if(numAsteroids <= 0) {
setUpNextLevel();
}
if(!paused) {
ship.move(dim.width, dim.height);
//move the ship
for(int i = 0; i < numShots; i++) {
shots[i].move(dim.width, dim.height);
if(shots[i].getLifeLeft() <= 0) {
deleteShot(i);
i--;
}
}
updateAsteroids();
if(shooting && ship.canShoot()) {
//add a shot on to the array
shots[numShots] = ship.shoot();
numShots++;
}
}
repaint();
try {
endTime = System.currentTimeMillis();
if(framePeriod - (endTime - startTime) > 0) {
Thread.sleep(framePeriod - (endTime - startTime));
}
}
catch(InterruptedException e) {
}
}
}
private void deleteShot(int index) {
//delete shot and move all shots after it up in the array
numShots--;
for(int i = index; i < numShots; i++) {
shots[i] = shots[i + 1];
shots[numShots] = null;
}
}
private void deleteAsteroid(int index) {
//delete asteroid and shift ones after it up in the array
numAsteroids--;
for(int i = index; i < numAsteroids; i++) {
asteroids[i] = asteroids[i + 1];
asteroids[numAsteroids] = null;
}
}
private void addAsteroid(Asteroid ast) {
//adds asteroid in at end of array
asteroids[numAsteroids] = ast;
numAsteroids++;
}
private void updateAsteroids() {
for(int i = 0; i < numAsteroids; i++) {
// move each asteroid
asteroids[i].move(dim.width, dim.height);
//check for collisions with the ship
if(asteroids[i].shipCollision(ship)) {
level--;
//restart this level
numAsteroids = 1;
return;
}
//check for collisions with any of the shots
for(int j = 0; j < numShots; j++) {
if(asteroids[i].shotCollision(shots[j])) {
//if the shot hit an asteroid, delete the shot
deleteShot(j);
//split the asteroid up if needed
if(asteroids[i].getHitsLeft() > 1) {
for(int k = 0; k < asteroids[i].getNumSplit(); k++) {
addAsteroid(
asteroids[i].createSplitAsteroid(
minAstVel, maxAstVel));
}
}
//delete the original asteroid
deleteAsteroid(i);
j=numShots;
i--;
}
}
}
}
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ENTER) {
if(!ship.isActive() && !paused) {
ship.setActive(true);
}
else {
paused = !paused;
//enter is the pause button
if(paused) {
//grays out the ship if paused
ship.setActive(false);
}
else {
ship.setActive(true);
}
}
}
else if(paused || !ship.isActive()) {
return;
}
else if(e.getKeyCode() == KeyEvent.VK_SPACE) {
ship.setAccelerating(true);
}
else if(e.getKeyCode() == KeyEvent.VK_LEFT) {
ship.setTurningLeft(true);
}
else if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
ship.setTurningRight(true);
}
else if(e.getKeyCode() == KeyEvent.VK_CONTROL) {
shooting=true;
}
else if(e.getKeyCode() == KeyEvent.VK_M) {
audioClip.stop();
}
else if(e.getKeyCode() == KeyEvent.VK_S) {
audioClip.loop();
}
}
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_UP) {
ship.setAccelerating(false);
}
else if(e.getKeyCode() == KeyEvent.VK_LEFT) {
ship.setTurningLeft(false);
}
else if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
ship.setTurningRight(false);
}
else if(e.getKeyCode() == KeyEvent.VK_CONTROL) {
shooting=false;
}
}
public void keyTyped(KeyEvent e) {
}
}
this is a "clone" of Asteroids. Some of you who have been around maybe a little longer than myself have probably played it in an arcade. Anyways, I wrote this for a final project in my Java course, but could never get to work completely. It starts up just fine, you can turn and shoot, but not move forward. I'm not worried about moving right now though. What happens after applet starts is music plays and an "asteroid" travels across the screen. If you shoot the asteroid or shoot 3 times in any direction, a thread exception occurs.
Exception in thread "Thread-3" java.lang.NullPointerException
at AsteroidsGame.updateAsteroids(AseroidsGame.java:161)
at AsteroidsGame.run(AsteroidsGame.java:115)
at java.lang.Thread.run(Thread.java:722)
Having looked around online has benefited me very little. Most of what I have learned was self taught and not as easy to wrap my head around. I'm not exactly sure what to do here to resolve this exception. In another thread on stack, it was mentioned to use an ArrayList because the array was not formerly declared with a size. Tinkered with that; never got it to work.
I need ideas/suggestions/criticism. I want to get this to work just so I can say I finished it.
other classes,
Asteroid.java
import java.awt.*;
public class Asteroid {
double x, y, xVelocity, yVelocity, radius;
int hitsLeft, numSplit;
public Asteroid(double x,double y,double radius,double minVelocity, double maxVelocity,int hitsLeft,int numSplit) {
this.x = x;
this.y = y;
this.radius = radius;
this.hitsLeft = hitsLeft;
this.numSplit = numSplit;
double vel = minVelocity + Math.random() * (maxVelocity - minVelocity);
double dir = 2 * Math.PI * Math.random();
xVelocity = vel * Math.cos(dir);
yVelocity = vel * Math.sin(dir);
}
public void move(int scrnWidth, int scrnHeight) {
x += xVelocity;
y += yVelocity;
if(x < 0 - radius) {
x += scrnWidth + 2 * radius;
}
else if(x > scrnWidth + radius) {
x -= scrnWidth + 2 * radius;
}
if(y < 0 - radius) {
y += scrnHeight + 2 * radius;
}
else if(y > scrnHeight + radius) {
y -= scrnHeight + 2 * radius;
}
}
public void draw(Graphics g) {
g.setColor(Color.gray);
g.fillOval((int)(x - radius + .5), (int)(y - radius + .5), (int)(2 * radius), (int)(2 * radius));
}
public Asteroid createSplitAsteroid(double minVelocity,double maxVelocity) {
return new Asteroid(x, y, radius / Math.sqrt(numSplit), minVelocity, maxVelocity, hitsLeft - 1, numSplit);
}
public boolean shipCollision(Ship ship) {
if(Math.pow(radius + ship.getRadius(), 2) > Math.pow(ship.getX() - x, 2) + Math.pow(ship.getY() - y, 2) && ship.isActive()) {
return true;
}
return false;
}
public boolean shotCollision(Shot shot) {
if(Math.pow(radius, 2) > Math.pow(shot.getX() - x, 2) + Math.pow(shot.getY() - y, 2)) {
return true;
}
return false;
}
public int getHitsLeft() {
return hitsLeft;
}
public int getNumSplit() {
return numSplit;
}
}
Ship.java
import java.awt.*;
public class Ship {
final double[] origXPts = {14,-10,-6,-10}, origYPts = {0,-8,0,8}, origFlameXPts = {-6,-23,-6}, origFlameYPts = {-3,0,3};
final int radius = 6;
double x, y, angle, xVelocity, yVelocity, acceleration, velocityDecay, rotationalSpeed;
//used for movement
boolean turningLeft, turningRight, accelerating, active;
int [] xPts, yPts, flameXPts, flameYPts;
//current location of ship
int shotDelay, shotDelayLeft;
//rate of fire
public Ship(double x, double y, double angle, double acceleration, double velocityDecay, double rotationalSpeed, int shotDelay) {
this.x = x;
this.y = y;
this.angle = angle;
this.acceleration = acceleration;
this.velocityDecay = velocityDecay;
this.rotationalSpeed = rotationalSpeed;
xVelocity = 0;
yVelocity = 0;
turningLeft = false;
turningRight = false;
accelerating = false;
active = false;
xPts = new int[4];
yPts = new int[4];
flameXPts = new int[3];
flameYPts = new int[3];
this.shotDelay = shotDelay;
shotDelayLeft = 0;
}
public void draw(Graphics g) {
if(accelerating && active) {
for(int i = 0; i < 3; i++) {
flameXPts[i] = (int)(origFlameXPts[i] * Math.cos(angle) - origFlameYPts[i] * Math.sin(angle) + x + .5);
flameYPts[i] = (int)(origFlameXPts[i] * Math.sin(angle) + origFlameYPts[i] * Math.cos(angle) + y + .5);
}
g.setColor(Color.red);
//color of flame
g.fillPolygon(flameXPts, flameYPts, 3);
}
for(int i = 0; i < 4; i++) {
xPts[i] = (int)(origXPts[i] * Math.cos(angle) - origYPts[i] * Math.sin(angle) + x + .5);
yPts[i] = (int)(origXPts[i] * Math.sin(angle) + origYPts[i] * Math.cos(angle) + y + .5);
}
if(active) {
g.setColor(Color.white);
}
else {
g.setColor(Color.darkGray);
}
g.fillPolygon(xPts, yPts, 4);
}
public void move(int scrnWidth, int scrnHeight) {
if(shotDelayLeft > 0) {
shotDelayLeft--;
}
if(turningLeft) {
angle -= rotationalSpeed;
}
if(turningRight) {
angle += rotationalSpeed;
}
if(angle > (2 * Math.PI)) {
angle -= (2 * Math.PI);
}
else if(angle < 0) {
angle += (2 * Math.PI);
}
if(accelerating) {
xVelocity += acceleration * Math.cos(angle);
yVelocity += acceleration * Math.sin(angle);
}
x += xVelocity;
y += yVelocity;
xVelocity *= velocityDecay;
yVelocity *= velocityDecay;
if(x<0) {
x += scrnWidth;
}
else if(x > scrnWidth) {
x -= scrnWidth;
}
if(y < 0) {
y += scrnHeight;
}
else if(y > scrnHeight) {
y -= scrnHeight;
}
}
public void setAccelerating(boolean accelerating) {
this.accelerating = accelerating;
//start or stop accelerating the ship
}
public void setTurningLeft(boolean turningLeft) {
this.turningLeft = turningLeft;
//start or stop turning the ship
}
public void setTurningRight(boolean turningRight) {
this.turningRight = turningRight;
}
public double getX() {
return x;
//returns the ship’s x location
}
public double getY() {
return y;
//returns the ship's y location
}
public double getRadius() {
return radius;
//returns radius of circle that approximates the ship
}
public void setActive(boolean active) {
this.active = active;
//used when the game is paused or unpaused
}
public boolean isActive() {
return active;
}
public boolean canShoot() {
if(shotDelayLeft > 0) {
return false;
}
else {
return true;
}
}
public Shot shoot() {
shotDelayLeft=shotDelay;
//set delay till next shot can be fired
//a life of 40 makes the shot travel about the width of the
//screen before disappearing
return new Shot(x, y, angle, xVelocity, yVelocity, 40);
}
}
Shot.java
import java.awt.*;
public class Shot {
final double shotSpeed = 12;
double x, y, xVelocity, yVelocity;
int lifeLeft;
public Shot(double x, double y, double angle, double shipXVel, double shipYVel, int lifeLeft) {
this.x = x;
this.y = y;
xVelocity = shotSpeed * Math.cos(angle) + shipXVel;
yVelocity = shotSpeed * Math.sin(angle) + shipYVel;
this.lifeLeft = lifeLeft;
}
public void move(int scrnWidth, int scrnHeight) {
lifeLeft--;
x += xVelocity;
y += yVelocity;
if(x < 0) {
x += scrnWidth;
}
else if(x > scrnWidth) {
x -= scrnWidth;
}
if(y < 0) {
y += scrnHeight;
}
else if(y > scrnHeight) {
y -= scrnHeight;
}
}
public void draw(Graphics g) {
g.setColor(Color.yellow);
g.fillOval((int)(x - .5), (int)(y - .5), 3, 3);
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public int getLifeLeft() {
return lifeLeft;
}
}
thanks for looking.
---------------------Edit 12/9/2012------------------------
Tried debugging with NetBeans IDE 7.2.1. It improperly depicts the turning of ship and gives a completely different error:
Exception in thread "Thread-3" java.lang.NullPointerException
at AsteroidsGame.run(AsteroidsGame.java:122)
at java.lang.Thread.run(Thread.java:722)
It is directing attention to the Shot[] shots array inside the run method.
This could be expected as the array for Asteroid[] and Shot[] are set up the same.
Alas this is a university type question, but one im struggling with none the less.
I need to generate large test sets of rectangles, non-overlapping, that can then be used to test an algorithm that finds the adjacencies between them. The test sets will need to probably have upwards of 10000 - 100000 rectangles in them. Ive been searching the net for examples of how to generate test sets like this, but have come up with nothing. Im aware that I could use a purely brute force method, and every time a random rectangle is generated, check whether or not it overlaps with any of the other rectangles, but this seems like generating the test sets will end up taking days if not longer!
Any one know how to go about doing this, or at least where I should start looking?
I found your idea fun and challanging and therefore tried a solution by using a matrix.
Basicly, the idea is (when talking in pixels) to create a matrix of booleans of the same width and height as the square root of MaxWidthOfRectangle * (NumberOfRectangles) (just for simplicity the same width and height).
Next, for each entry in the matrix, generate a random rectangle between min and max bounds. and set all the bools in the matrix for the specific rectangle. Now when generating the next rectangle, you can simply check "around" the desired location to determine how much space you can occupy rather then having to estimate a size and compare with each other rectangle if it conflicts.
My code:
class RectangleGenerator
{
readonly bool[,] _matrix;
readonly int _size;
readonly int _minimalBoxSize;
readonly int _maximumBoxSize;
readonly Random _random = new Random(1);
readonly List<Point> _offsets;
public bool[,] Matrix { get { return _matrix; } }
public RectangleGenerator(int size, int minimalBoxSize, int maximumBoxSize)
{
_matrix = new bool[size, size];
_size = size;
_minimalBoxSize = minimalBoxSize;
_maximumBoxSize = maximumBoxSize;
_offsets = new List<Point>(size * size);
Reset();
}
public IEnumerable<Rectangle> Calculate()
{
while (_offsets.Count > 0)
{
Point currentPoint = _offsets[_offsets.Count - 1];
_offsets.RemoveAt(_offsets.Count - 1);
if (!_matrix[currentPoint.X, currentPoint.Y])
{
Rectangle rectangle;
if (TryCreateNextRectangle(currentPoint.X, currentPoint.Y, out rectangle))
{
// fill the matrix with the rectangle + padding
int startFillX = Math.Max(0, rectangle.Left);
int startFillY = Math.Max(0, rectangle.Top);
int endFillX = Math.Min(_size, rectangle.Right);
int endFillY = Math.Min(_size, rectangle.Bottom);
for (int fillX = startFillX; fillX < endFillX; fillX++)
for (int fillY = startFillY; fillY < endFillY; fillY++)
{
_matrix[fillX, fillY] = true;
}
yield return rectangle;
}
}
}
}
private bool TryCreateNextRectangle(int x, int y, out Rectangle rectangle)
{
int maxWidth = DetermineMaxWidth(x, y, _minimalBoxSize);
int maxHeight = DetermineMaxHeight(y, x, maxWidth);
if (maxWidth < _minimalBoxSize || maxHeight < _minimalBoxSize)
{
rectangle = Rectangle.Empty;
return false;
}
int width = _random.Next(_minimalBoxSize, maxWidth);
int height = _random.Next(_minimalBoxSize, maxHeight);
rectangle = new Rectangle(x, y, width, height);
return true;
}
private int DetermineMaxWidth(int x, int y, int height)
{
int result = Math.Min(_maximumBoxSize, _size - x);
for (int offsetX = 0; offsetX < result; offsetX++)
for (int offsetY = 0; offsetY < height; offsetY++)
{
if (_matrix[x + offsetX, y + offsetY])
{
result = offsetX;
break;
}
}
return result;
}
private int DetermineMaxHeight(int y, int x, int width)
{
int result = Math.Min(_maximumBoxSize, _size - y);
for (int offsetY = 0; offsetY < result; offsetY++)
for (int offsetX = 0; offsetX < width; offsetX++ )
{
if (_matrix[x + offsetX, y + offsetY])
{
result = offsetY;
break;
}
}
return result;
}
public void Reset()
{
// append for padding:
for (int x = 0; x < _size; x++)
for (int y = 0; y < _size; y++)
{
_matrix[x, y] = false;
if (_size - x >= _minimalBoxSize && _size - y >= _minimalBoxSize)
{
_offsets.Add(new Point(x, y));
}
}
_offsets.Sort((x, y) => x == y ? 0 : _random.Next(-1, 1));
}
}
So I've been working in Processing for a few weeks now, and, though I'm not experienced in programming, I have moved on to more complex projects. I'm programming an evolution simulator, that spawns creatures with random properties.
Eventually, I'll add reproduction, but as of now the creatures just sort of float around the screen, and follow the mouse somewhat. It interacts with sound from the line in, but I commented those parts out so that it can be viewed on the canvas, it shouldn't really change the question, I just thought I would point it out.
As of now, the framerate is far less than ideal for me, and it slowly lowers as more creatures are spawned. Am I making some fundamental mistake, or am I just running too many functions per frame?
Here's the source code, and you can play with it in the browser here:
//import ddf.minim.*;
//import ddf.minim.signals.*;
//import ddf.minim.analysis.*;
//import ddf.minim.effects.*;
//Minim minim;
//AudioInput in;
boolean newCreature = true;
boolean matured[];
int ellipses[];
int hair[];
int maxCreatureNumber = 75;
//int volume;
//int volumeTolerance = 1;
int creatureIndex = -1;
int creatureX[];
int creatureY[];
float strokeWeightAttribute[];
float creatureSize[];
float creatureEndSize[];
float creatureXIncrement[];
float creatureYIncrement[];
float bubbleSize;
float easing = 0.05;
float angle = 0.00;
color colorAttribute[];
void setup() {
background(0);
size(1000,500);
noFill();
//minim = new Minim(this);
//in = minim.getLineIn(Minim.STEREO, 512);
creatureX = new int[maxCreatureNumber];
creatureY = new int[maxCreatureNumber];
ellipses = new int[maxCreatureNumber];
hair = new int[maxCreatureNumber];
strokeWeightAttribute = new float[maxCreatureNumber];
creatureEndSize = new float[maxCreatureNumber];
creatureSize = new float[maxCreatureNumber];
creatureXIncrement = new float[maxCreatureNumber];
creatureYIncrement = new float[maxCreatureNumber];
matured = new boolean[maxCreatureNumber];
colorAttribute = new color[maxCreatureNumber];
}
void draw() {
angle += 0.05;
fill(0, 50);
rect(-1, -1, 1001, 501);
// for(int i = 0; i < in.bufferSize() - 1; i++) {
// if(in.mix.get(i) * 50 > volumeTolerance) {
// volume++;
// }
// }
if(newCreature && creatureIndex < maxCreatureNumber - 1) {
initSpontaneousCreature();
}
updateCreatures();
// bubbleSize = volume/250;
bubbleSize += 0.01;
// volume = 0;
}
//void stop() {
// minim.stop();
// super.stop();
//}
void initSpontaneousCreature() {
creatureIndex++;
creatureEndSize[creatureIndex] = int(random(5, 20));
creatureX[creatureIndex] = int(random(1000));
if(creatureX[creatureIndex] >= 500) {
creatureX[creatureIndex] -= creatureEndSize[creatureIndex];
}
else {
creatureX[creatureIndex] += creatureEndSize[creatureIndex];
}
creatureY[creatureIndex] = int(random(500));
if(creatureY[creatureIndex] >= 250) {
creatureY[creatureIndex] -= creatureEndSize[creatureIndex];
}
else {
creatureY[creatureIndex] += creatureEndSize[creatureIndex];
}
ellipses[creatureIndex] = int(random(4));
hair[creatureIndex] = int(random(4));
strokeWeightAttribute[creatureIndex] = random(1, 4);
colorAttribute[creatureIndex] = color(int(random(20,255)), int(random(20,255)), int(random(20,255)));
matured[creatureIndex] = false;
newCreature = false;
while(ellipses[creatureIndex] == 0 && hair[creatureIndex] == 0) {
ellipses[creatureIndex] = int(random(4));
hair[creatureIndex] = int(random(4));
}
}
void updateCreatures() {
for(int n = 0; n <= creatureIndex; n++) {
if(matured[n]) {
creatureX[n] += ((((mouseX) - creatureX[n]) * easing) / (60/*-abs(volume/5))*/)) + random(-5, 6);
creatureY[n] += ((((mouseY) -creatureY[n]) * easing) / (60/*-abs(/*volume/5))*/)) + random(-5,6);
drawCreature();
}
else {
if(creatureEndSize[n] != creatureSize[n]) {
creatureSize[n] += bubbleSize;
if(creatureSize[n] > creatureEndSize[n]) {
creatureSize[n] -= (creatureSize[n] - creatureEndSize[n]);
}
}
else {
newCreature = true;
matured[n] = true;
// bubbleSize = 0;
}
drawCreature();
}
}
}
void drawCreature() {
for(int n = 0; n <= creatureIndex; n++) {
if(matured[n]) {
stroke(colorAttribute[n]);
strokeWeight(strokeWeightAttribute[n]);
for(int i = 0; i <= 4; i++) {
if(ellipses[n] == i) {
if(i == 0) {
}
else if (i == 1) {
pushMatrix();
translate(creatureX[n], creatureY[n]);
ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
rotate(radians(180));
ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
popMatrix();
}
else if(i == 2) {
pushMatrix();
translate(creatureX[n], creatureY[n]);
ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
rotate(radians(180));
ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
rotate(radians(270));
ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
popMatrix();
}
else if(i == 3) {
pushMatrix();
translate(creatureX[n], creatureY[n]);
ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
rotate(radians(90));
ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
rotate(radians(180));
ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
rotate(radians(270));
ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
popMatrix();
}
}
if(hair[n] == i) {
if(i == 0) {
}
else if (i == 1) {
pushMatrix();
translate(creatureX[n], creatureY[n]);
for(int j = 0; j <= 360; j+=70) {
rotate(j);
stroke(colorAttribute[n], random(255));
line(0,0, creatureSize[n] + random(10), creatureSize[n] + random(10));
}
popMatrix();
}
else if(i == 2) {
pushMatrix();
translate(creatureX[n], creatureY[n]);
for(int j = 0; j <= 360; j+=30) {
rotate(j);
stroke(colorAttribute[n], random(255));
line(0,0, creatureSize[n] + random(10), creatureSize[n] + random(10));
}
popMatrix();
}
else if(i == 3) {
pushMatrix();
translate(creatureX[n], creatureY[n]);
for(int j = 0; j <= 360; j+=1) {
rotate(j);
stroke(colorAttribute[n], random(255));
line(0,0, creatureSize[n] + random(10), creatureSize[n] + random(10));
}
popMatrix();
}
}
}
}
if(!matured[n]) {
stroke(abs(sin(angle) * 255));
//strokeWeight(5);
ellipse(creatureX[n], creatureY[n], creatureSize[n] * 5, creatureSize[n] * 5);
noStroke();
}
}
}
Right, as I suspected, all the unnecessary pushMatrix(), popMatrix() calls and the large amount of lines seemed to be the main culprits, still, there was a lot of redundant code.
I simply refactored the code in a cleaner manner and it seems to run fine.
Here is my 'improved' version:
int maxCreatures = 75;
int numCreatures = 0;
int spawnNthFrame = 50;//spawn a creature every 50 frames
Creature[] creatures;
void setup() {
background(0);
size(1000,500);
noFill();
creatures = new Creature[maxCreatures];
}
void draw() {
fill(0, 50);
rect(-1, -1, 1001, 501);
if(frameCount % spawnNthFrame == 0){
println("creatures: " + numCreatures);
if(numCreatures < maxCreatures) {
//Creature constructor float endSize,int x, int y,int ellipses,int hair,float strokeW,color c
creatures[numCreatures] = new Creature(random(5, 20),int(random(1000)),int(random(500)),int(random(4)),int(random(4)),random(1, 4),color(int(random(20,255)), int(random(20,255)), int(random(20,255))));
numCreatures++;
}
}
for(int i = 0; i < numCreatures; i++) creatures[i].update();
}
and the Creature class:
class Creature{
int x,y,cXInc,cYInc;//if x,y are ints, increments would be into, right?
float cStrokeWeight,cSize,cEndSize,cSizeInc = 0.01,easing = 0.05,angle = 0.00;
color cColor;
int hair,numHair,ellipses;
boolean matured = false;
Creature(float endSize,int x, int y,int ellipses,int hair,float strokeW,color c){
cEndSize = endSize;
this.x = x;
if(x >= 500) x -= cEndSize;
else x += cEndSize;
this.y = y;
if(y >= 250) x -= cEndSize;
else x += cEndSize;
this.ellipses = ellipses;
this.hair = hair;
if(hair == 1) numHair = 3;//~5, half that, draw through centre, etc.
if(hair == 2) numHair = 6;
if(hair == 3) numHair = 30;//no default value
cStrokeWeight = strokeW;
this.cColor = c;
}
void update(){
if(matured) {
x += (((mouseX - x) * easing) / 60) + random(-5, 6);
y += (((mouseY - y) * easing) / 60) + random(-5, 6);
}else {
if(cSize < cEndSize) cSize += cSizeInc;
else matured = true;
angle += 0.05;
}
this.draw();
}
void draw(){
if(matured){
stroke(cColor);
strokeWeight(cStrokeWeight);
if(ellipses == 1){//2 ellipses diagonally
ellipse(x,y,cSize,cSize);
ellipse(x+cSize,y+cSize,cSize,cSize);
}
if(ellipses == 2){
ellipse(x,y,cSize,cSize);
ellipse(x,y+cSize,cSize,cSize);
ellipse(x+cSize,y+cSize,cSize,cSize);
}
if(ellipses == 3){
ellipse(x,y,cSize,cSize);
ellipse(x+cSize,y,cSize,cSize);
ellipse(x,y+cSize,cSize,cSize);
ellipse(x+cSize,y+cSize,cSize,cSize);
}
float hairAngleInc = TWO_PI/numHair;//angle increment for each piece = 360/number of hair lines
float hairAngle,hairLength,hairCos,hairSin;
for(int i = 0; i < numHair; i++){
hairAngle = hairAngleInc * i;
hairCos = cos(hairAngle);
hairSin = sin(hairAngle);
hairLength = random(20);
stroke(cColor, random(255));
line(x + (hairCos * -hairLength),y + (hairSin * -hairLength), x + (hairCos * hairLength),y + (hairSin * hairLength));
}
}else{
stroke(abs(sin(angle) * 255));
ellipse(x,y, cSize * 5, cSize * 5);
}
}
}
Ok, now for the explanations.
First, I separated all the variables that were related to one creature from the 'global' ones that determine how the sketch runs (how many creatures get spawned, etc.).
This makes the main code about 25 lines long and altogether a bit below 100 lines which is less than half of the original.
The first part doesn't do anything special. In the draw() function, instead of creating a Creature every frame, I draw one every Nth frame using the spawnNthFrame variable, this made it easy to see which state of the creature made it slow. If you set a small number like 2 to that variable it should spawn a lot of creatures per frame.
The Creature class has all the properties the original code stored in arrays.
Instead of doing
pushMatrix();
translate();
ellipse();
rotate()
ellipse()
popMatrix();
I simply draw the ellipses at x,y.
A little hint on the rotations. I've noticed they were increments
of 90 degrees. Processing has some nice constants for 90,180,360 degrees
in radians: HALF_PI, PI, TWO_PI which can be handy sometimes.
Now for the 'hairy' situation, here's something I commented out for myself:
//if(i == 1) for(int j = 0; j <= 360; j+=70) , well 360/70 is about 5, if (i == 2) , 12 hair
//if = 3-> 360 lines ? do you really need that many lines, that thick ? how about 30 ? 5*12=60, but if you draw the lines through the centre, not from the centre, you can get away with half the lines
So there were 3 loops for drawing lines, each having different increments. Basically
there were either 360/70 lines, 360/30 lines and 360 lines.
Roughly about 5,12 and 360 lines. About the 5,12 lines, I kind of halved that by drawing 'diameter' lines across the centre as opposed to 'radius' lines from the centre.
Here's what I mean,
Also I think that 360 lines with that strokeWeight and the jittery motion will probably look like a bunch of lines hard to count, so I thought, why split hairs? :P
Maybe the creature will look pretty similar with about 60 radii which means 30 diameters.
Now to explain a bit of the trig functions used for this.
The main thing is the 'polar to cartesian' coordinates conversion:
Polar would be something like:
"I am moving on a circle to a direction described by an angle (much like one handle of a clock) and radius (distance from centre)."
and Cartesian
"I'm moving based on two axes (horizontal/X and vertical/Y), kind of like the streets of Manhattan, but I cheat and also move diagonally through walls."
If that makes any sense... :)
Anyway, you convert the angle and radius pair to the x and y pair using the formula:
x = cos(angle) * radius
y = sin(angle) * radius
For each line:
angle = hairAngle
radius = hairLength
So the line() with *x + (hairCos * -hairLength)* looks a bit like this:
x + (hairCos * -hairLength) =
move to x and from there move by hairLength
to the left(-) for the current angle (hairCos)
Similar for y, but using cos, so this puts the first point of the line in the opposite direct (-hairLength) of the angle moving from the centre (which is the Creature's x) and the second is 'diagonal'. Imagine drawing 'diagonals' (from (-x,-y) to (+x,+y)), but you also rotate these.
Update
Apparently copy/pasting this code works in javascript too (best viewed in Chromium/Chrome). You can also run it right here:
var maxCreatures = 75;
var numCreatures = 0;
var spawnNthFrame = 50;//spawn a creature every 50 frames
var creatures = [];
function setup() {
background(0);
createCanvas(1000,500);
noFill();
}
function draw() {
fill(0, 50);
rect(-1, -1, 1001, 501);
if(frameCount % spawnNthFrame === 0){
println("creatures: " + numCreatures);
if(numCreatures < maxCreatures) {
//Creature constructor float endSize,int x, int y,int ellipses,int hair,float strokeW,color c
creatures[numCreatures] = new Creature(random(5, 20),int(random(1000)),int(random(500)),int(random(4)),int(random(4)),random(1, 4),color(int(random(20,255)), int(random(20,255)), int(random(20,255))));
numCreatures++;
}
}
for(var i = 0; i < numCreatures; i++) creatures[i].update();
}
function Creature(endSize,x,y,ellipses,hair,strokeW,c){
this.x = x;
this.y = y;
this.ellipses = ellipses;
this.hair = hair;
this.numHair = 0;
this.cStrokeWeight = strokeW;
this.cColor = c;
this.cXInc = 0;
this.cYInc = 0.01;
this.cSize = 0;
this.cEndSize = endSize;
this.easing = 0.05;
this.angle = 0.0;
this.matured = false;
if(x >= 500) x -= this.cEndSize;
else x += this.cEndSize;
if(y >= 250) x -= this.cEndSize;
else x += this.cEndSize;
if(hair == 1) this.numHair = 3;//~5, half that, draw through centre, etc.
if(hair == 2) this.numHair = 6;
if(hair == 3) this.numHair = 30;//no default value
this.update = function(){
if(this.matured) {
this.x += (((mouseX - this.x) * this.easing) / 60) + random(-5, 6);
this.y += (((mouseY - this.y) * this.easing) / 60) + random(-5, 6);
}else {
if(this.cSize < this.cEndSize) this.cSize += this.cSizeInc;
else this.matured = true;
this.angle += 0.05;
}
this.draw();
}
this.draw = function(){
if(this.matured){
stroke(this.cColor);
strokeWeight(this.cStrokeWeight);
if(this.ellipses == 1){//2 ellipses diagonally
ellipse(this.x,this.y,this.cSize,this.cSize);
ellipse(this.x+this.cSize,this.y+this.cSize,this.cSize,this.cSize);
}
if(this.ellipses == 2){
ellipse(this.x,this.y,this.cSize,this.cSize);
ellipse(this.x,this.y+this.cSize,this.cSize,this.cSize);
ellipse(this.x+this.cSize,this.y+this.cSize,this.cSize,this.cSize);
}
if(this.ellipses == 3){
ellipse(this.x,this.y,this.cSize,this.cSize);
ellipse(this.x+this.cSize,this.y,this.cSize,this.cSize);
ellipse(this.x,this.y+this.cSize,this.cSize,this.cSize);
ellipse(this.x+this.cSize,this.y+this.cSize,this.cSize,this.cSize);
}
var hairAngleInc = TWO_PI/this.numHair;//angle increment for each piece = 360/number of hair lines
var hairAngle,hairLength,hairCos,hairSin;
for(var i = 0; i < this.numHair; i++){
hairAngle = hairAngleInc * i;
hairCos = cos(hairAngle);
hairSin = sin(hairAngle);
hairLength = random(20);
stroke(this.cColor, random(255));
line(this.x + (hairCos * -hairLength),this.y + (hairSin * -hairLength), this.x + (hairCos * hairLength),this.y + (hairSin * hairLength));
}
}else{
stroke(abs(sin(this.angle) * 255));
ellipse(this.x,this.y, this.cSize * 5, this.cSize * 5);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.4/p5.min.js"></script>
You could use the frameRate(fps)function. What it does is, it specifies the number of frames to be displayed every second. However, If the processor is not fast enough to maintain the specified rate, it will not be achieved. For example, the function call frameRate(30) will attempt to refresh 30 times a second. It is recommended to set the frame rate within setup().
Remember, using draw() without specifying the frame rate, by default it will run at 60 fps.
Well, there's the good old random-pause method. It's the "poor man's profiler".
Just snapshot it a few times. That will show you exactly what's taking the most time. Those are the things you should see if you can make faster.
It will show up in increased framerate.