how to optimize this?

hi,

here is a prototype of a simple java game inspired by anaconda in timesplitters 2 - the only problem is if you make the play area too large, or your snake gets too long then the whole thing slows down far too much!

I can't figure how to speed this up, but am sure there are people in this forum who can..

thanks,

asjf

import javax.swing.*;

import java.util.*;

import java.awt.Color;

import java.awt.Rectangle;

import java.awt.Point;

import java.awt.Font;

import java.awt.Graphics;

import java.awt.RenderingHints;

import java.awt.Dimension;

import java.awt.Graphics2D;

import java.awt.image.*;

import java.awt.event.*;

publicclass InteractiveTestextends JPanelimplements Runnable//, SnakeListener

{

public Vector snakes;

Vector food;

Color foodColor;

int maxFood = 8;

Thread myThread;

Random random;

BufferedImageoffscreen;

Graphics2Dgoff;

Dimensiond;

int state;

finalint IN_GAME = 1;

finalint DEAD = 2;

finalint WAITING = 0;

public InteractiveTest()

{

super();

super.setBackground(Color.black);

myThread =new Thread(this);

random =new Random();

snakes =new Vector();

foodColor = Color.red;

for(int i=0; i<1; i++)

snakes.add(new Snake(new Color(random.nextInt()), KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT,50+(i*10),250+(i*10)) );

food =new Vector();

state = IN_GAME;

}

publicvoid run()

{

offscreen = (BufferedImage) createImage(d.width-50, d.height-50);

goff = (Graphics2D) offscreen.getGraphics();

goff.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_SPEED);

goff.setBackground(Color.black);

//while(state==WAITING){}

try

{

while(state!=DEAD)

{

paintGame(getGraphics());

for(int i=0; i<snakes.size(); i++)

((Snake) snakes.get(i)).collisionDetect();

if(state!=DEAD)

for(int i=0; i><snakes.size(); i++)

((Snake) snakes.get(i)).move();

if(random.nextInt(2000)==5)

{

food.add(new Point(random.nextInt(d.width-50),random.nextInt(d.height-50)));

if(food.size()==maxFood)

food.remove(0);

}

}

repaint();

}

catch(Exception e)

{

e.printStackTrace();

}

}

publicvoid paint(Graphics g)

{

if(g==null || d==null)return;

super.paint(g);

g.setColor(Color.white);

g.drawRect(24,24,d.width-48,d.height-48);

g.setFont(new Font("Verdana",Font.BOLD,12));

g.drawString("Anaconda",200,20);

g.drawString(""+((Snake)snakes.get(0)).score,350,20);

if(state==DEAD)

{

paintGame(g);

g.drawString("Game Over",(d.width-48)/2,12+(d.height-48)/2);

}

}

publicvoid paintGame(Graphics g)

{

goff.clearRect(0,0,d.width-50,d.height-50);

for(int i=0; i><snakes.size(); i++)

((Snake) snakes.get(i)).paint(goff);

goff.setColor(foodColor);

for(int i=0; i><food.size(); i++)

goff.fillOval((int)((Point) food.get(i)).getX(),(int)((Point) food.get(i)).getY(),9,9);

g.drawImage(offscreen, 25, 25,this);

}

/*Point rotate(Point p, Point origin, double radians)

{

double cos = Math.cos(radians), sin = Math.sin(radians);

double x = p.getX();

double y = p.getY();

return new Point(((int) (origin.getX() + x * cos - y * sin)),

((int) (origin.getY() + y * cos + x * sin))

);

}*/

/*########### Snake ###########*/

class Snakeimplements KeyListener

{

List pos;

Color snakeColor;

int score = 0;

int jump = 75, size=1000;

float wid = (float) 45.0, angle = (float)Math.PI, dangle =0;

float x,y, dx=(float)Math.sin((double)angle)/(float)10, dy=(float)Math.cos((double)angle)/(float)10;

int leftKey, rightKey;

boolean leftDown, rightDown;

Snake(Color c,int l,int r,float x,float y)

{

snakeColor = c;

leftKey = l;

rightKey = r;

this.x=x;

this.y=y;

pos =new ArrayList();

for(int i=0; i><size; i++)

move();

}

class Segment

{

Point a,b;

float xx,yy,dxx,dyy;

Segment(float x,float y,float dx,float dy)

{

xx=x;yy=y;dxx=dx;dyy=dy;

float xxx = dx*wid;

float yyy = dy*wid;

a =new Point((int) (x - yyy),(int) (y + xxx));

b =new Point((int) (x + yyy),(int) (y - xxx));

}

Segment getDownSized(float f){returnnew Segment(xx,yy,dxx*f,dyy*f);}

}

publicvoid keyTyped(KeyEvent e){}

publicvoid keyPressed(KeyEvent e)

{

if(e.getKeyCode()==leftKey)leftDown =true;

if(e.getKeyCode()==rightKey)rightDown =true;

if(e.getKeyCode()==KeyEvent.VK_Z)size*=2;

if(e.getKeyCode()==KeyEvent.VK_Q)jump+=8;

if(e.getKeyCode()==KeyEvent.VK_A)jump-=8;

updateKey();

}

publicvoid keyReleased(KeyEvent e)

{

if(e.getKeyCode()==leftKey)leftDown =false;

if(e.getKeyCode()==rightKey)rightDown =false;

updateKey();

}

publicvoid updateKey()

{

if(leftDown)dangle=(float)((float)0.38 * 2 * Math.PI) / (float) 360;

if(rightDown)dangle=(float)((float)-0.38 * 2 * Math.PI) / (float) 360;

if((leftDown && rightDown) || (!leftDown && !rightDown)) dangle=0;

}

void move()

{

angle+=dangle;

x+= (dx = (float) Math.sin((double)angle)/10);

y+= (dy = (float) Math.cos((double)angle)/10);

pos.add(new Segment(x,y,dx,dy));

if(pos.size()==size) pos.remove(0);

}

final Dimension foodSize =new Dimension(9,9);

void collisionDetect()

{

Segment snout = ((Segment) pos.get(pos.size()-(jump/3))).getDownSized((float).25);

Segment forehead = ((Segment) pos.get(pos.size()-(2*jump/3))).getDownSized((float)1.9);

int rgb;

int ps [][] =newint [][]

{

{(int)(snout.a.getX()), (int)(snout.a.getY())},

{(int)(snout.b.getX()), (int)(snout.b.getY())},

{(int)(forehead.a.getX()), (int)(forehead.a.getY())},

{(int)(forehead.b.getX()), (int)(forehead.b.getY())}

};

try

{

for(int i=0; i><ps.length; i++)

if((rgb = offscreen.getRGB(ps[i][0],ps[i][1]))!=Color.black.getRGB())

if(rgb!=foodColor.getRGB())

thrownew Exception("Dead");

else

for(int j=0; j><food.size(); j++)

if(new Rectangle((Point) food.get(j), foodSize).contains(ps[i][0],ps[i][1]))

{

Graphics g = getGraphics();

g.setFont(new Font("Verdana",Font.BOLD,12));

g.setColor(Color.black);

g.drawString(""+((Snake)snakes.get(0)).score,350,20);

score+=size;

size+=5*jump;

food.remove(j);

g.setColor(Color.white);

g.drawString(""+((Snake)snakes.get(0)).score,350,20);

}

}

catch(Exception e){state=DEAD;}

}

void paint(Graphics goff)

{

goff.setColor(snakeColor);

int magic = jump<<3;

for(int i=pos.size()-1; i>jump; i-=jump)

{

int j = i-jump;

Segment a = (Segment) pos.get(i), b = (Segment) pos.get(j);

if(i==pos.size()-1)

{

a = a.getDownSized((float)0.5);

b = b.getDownSized((float)1.6);

}

else

if(i==pos.size()-1-jump)

a = a.getDownSized((float)1.6);

else

if(i<magic)

{

a = a.getDownSized((float)1-(float)Math.pow((float)((float)(magic)-i)/(float)(magic),i/(jump<<1)));

b = b.getDownSized((float)1-(float)Math.pow((float)((float)(magic)-i)/(float)(magic),j/(jump<<1)));

}

/* ### for nobbly snake

else

{

a = a.getDownSized((float)0.7+(float)Math.abs((float)0.4*(float)Math.sin(i*(size/(5*jump))*Math.PI/pos.size())));

b = b.getDownSized((float)0.7+(float)Math.abs((float)0.4*(float)Math.sin(j*(size/(5*jump))*Math.PI/pos.size())));

}

*/

goff.drawLine((int)a.a.getX(),(int)a.a.getY(),(int)a.b.getX(),(int)a.b.getY());

goff.drawLine((int)a.a.getX(),(int)a.a.getY(),(int)b.a.getX(),(int)b.a.getY());

goff.drawLine((int)b.b.getX(),(int)b.b.getY(),(int)a.b.getX(),(int)a.b.getY());

}

}

}

/*########*/

publicstaticvoid main(String [] arg)throws Exception

{

InteractiveTest it =new InteractiveTest();

JFrame frame =new JFrame("Interactive Test");

frame.getContentPane().add(it);

frame.setSize(480,384);

frame.show();

for(int i=0; i><it.snakes.size(); i++)

frame.addKeyListener((Snake)it.snakes.get(i));

it.d = it.getSize();

it.myThread.run();

}

}

>

[18161 byte] By [asjfa] at [2007-9-28 7:40:08]
# 1
To tell you the truth, the code looks pretty poorly coded. You're creating a lot of objects, you seem to be throwing an Exception to get out of a loop etc. etc.
Kayamana at 2007-7-9 18:53:28 > top of Java-index,Other Topics,Java Game Development...
# 2
I can't see how not to create the number of objects that are currently created :(
asjfa at 2007-7-9 18:53:28 > top of Java-index,Other Topics,Java Game Development...
# 3

cool game :)

I think the main problem is that you're doing a lot of maths during the segment drawing, with a lot of type conversions. Perhaps you can use integers instead and use part of the integer bits as the fraction part. 1 becomes 1<<10 if the last 10 bits are the fraction, and to get pixel positions you can drop the fraction with (value>>10).

(float)1 creates an integer 1 and converts it to a float. 1F directly creates the same float.

Math.pow() works with double parameters and returns a double. Perhaps you should use double instead of integer to minimize the number of necessary type conversions.

Don't call run() to start a new thread, call start() (which in turn calls run() after creating a new thread for it).

the JPanel is not opaque by default (use setOpaque(true) to make it opaque). That's why the Frame's background color is used outside the playing area.

You create huge numbers of Segment objects while drawing the snake(s). A lot of time is wasted for their creation and the garbage collection. You should create 2 Segment objects instead which will then hold the downsized positions of the segments you are drawing.

Why do you have variables like (Segment).dxx ? Instead of using Point to represent your Segment corners, use float variables to minimize type conversion.

I would use a Segment class like this one if I was forced to use float :

class Segment

{

float ax, ay, bx, by;

float x, y, dx, dy;

Segment(float x,float y,float dx,float dy)

{

this.x = x;

this.y = y;

this.dx = dx;

this.dy = dy;

final float height = dx*wid;

final float width = dy*wid;

a = new Point((int) (x - width),(int) (y + height));

b = new Point((int) (x + width),(int) (y - height));

}

downsize(float f, Segment target)

{

target.x = x;

target.y = y;

target.dx = dx;

target.dy = dy;

target.a = a;

target.b = b;

}

}

You might try putting your snake in a LinkedList and drawing it using an Iterator to minimize the time it takes to remove the first element. Vector is synchronized. It takes significantly longer (or at least it used to) to call a synchronized method than to call an unsynchronized method because the synchronization mechanism uses up some time. ArrayList is an unsynchronized version of Vector. I would go so far as to try and keep my segments in an array of constant size. who will ever have a snake with 0x10000 elements? Let "segments" be the array, "tail" the position of the snake's tail within the array and "x" the index of the snake Segment you want to access. Your Segment would sit at segments[(x+tail)&0xFFFF].

Here's some changes I would make to the main() method :

public static void main(String [] arg) throws Exception

{

InteractiveTest it = new InteractiveTest();

JFrame frame = new JFrame("Interactive Test");

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.setContentPane(it); //frame.getContentPane().add(it);

frame.setSize(480,384);

frame.setVisible(true); //frame.show();

for(int i=0; i<it.snakes.size(); i++)

frame.addKeyListener((Snake)it.snakes.get(i));

it.d = it.getSize();

(new Thread(it)).start(); // it.myThread.run();

}

>

Zyphrusa at 2007-7-9 18:53:28 > top of Java-index,Other Topics,Java Game Development...
# 4
oops, i forgot to downsize in my downsize() method.Each line in the method should read something liketarget.x = f * x;
Zyphrusa at 2007-7-9 18:53:28 > top of Java-index,Other Topics,Java Game Development...
# 5

You do need to look at the number of new objects you are creating every frame. For instance in your paint loop you are creating a new Font object every time. You should create this font once at the start as a global and reference it from your paint loop.

Also every time you do a for loop, you are creating a new int i. Again globally declare an int called loop or something like and use that.

Do the same anywhere else you can in your main loop where are using new.

This will stop the GC kicking in all the time ,which will kill the speed of it.

Pjackoa at 2007-7-9 18:53:28 > top of Java-index,Other Topics,Java Game Development...
# 6
the point regarding object creation is a valid 1,however, removing the creation of loop counters from your loops will reduce, not improve performance. (and will also make your code ugly and unreadable)
Abusea at 2007-7-9 18:53:28 > top of Java-index,Other Topics,Java Game Development...
# 7

> To tell you the truth, the code looks pretty poorly

> coded. You're creating a lot of objects, you seem to

> be throwing an Exception to get out of a loop etc.

> etc.

Well, i should tell you, as far as i know trowing an error is the best way to get out of the loop.

I've learned that by reading Java Performance Tuning books, and it is way faster than checking everytime for a condition.

Now, if you do anything on the catch statement, then it is slower.

MaViZaoa at 2007-7-9 18:53:28 > top of Java-index,Other Topics,Java Game Development...
# 8
> Well, i should tell you, as far as i know trowing an> error is the best way to get out of the loop.But it's such ugly code for so little speed increase.
Kayamana at 2007-7-9 18:53:28 > top of Java-index,Other Topics,Java Game Development...
# 9

yeah, ok :) - I don't think its an important issue with regards to your other suggestion of creating fewer objects though?

I did an -Xprof run on the code and it gave

Interpreted + nativeMethod

13.9%0 +281sun.java2d.loops.DefaultComponent.IntIsomorphicCopy

4.9%0 +98sun.java2d.loops.IntDiscreteRenderer.devSetRect

Stub + nativeMethod

31.0%1 +624sun.java2d.loops.DefaultComponent.IntIsomorphicCopy

14.1%2 +283sun.java2d.loops.IntDiscreteRenderer.devSetRect

as the most time consuming code.

is it right to think this means its actually the rendering that is taking the most time?

(would making a GeneralPath for each graphics frame and rendering that instead of line by line be any use?)

asjfa at 2007-7-9 18:53:28 > top of Java-index,Other Topics,Java Game Development...
# 10

hi, i didn't spend much time looking at your code, but what i saw as a potential increas was:

do not compute same things over and over again.

//your code

goff.drawLine((int)a.a.getX(),(int)a.a.getY(),(int)a.b.getX(),(int)a.b.getY());

goff.drawLine((int)a.a.getX(),(int)a.a.getY(),(int)b.a.getX(),(int)b.a.getY());

goff.drawLine((int)b.b.getX(),(int)b.b.getY(),(int)a.b.getX(),(int)a.b.getY());

//my suggestion

int aax = (int)a.a.getX();

int aay = (int)a.a.getY();

int abx = (int)a.b.getX();

int aby = (int)a.b.getY();

int bax = (int)b.a.getX();

int bay = (int)b.a.getY();

int bbx = (int)b.b.getX();

int bby = (int)b.b.getY();

goff.drawLine(aax, aay, abx, aby);

goff.drawLine(aax, aay, bax, bay);

goff.drawLine(bbx, bby, abx, aby);

well, that's bad example, since i initialize more things that are needed, but you could consider doing that for at least aax, aay, abx and aby;

of course those four things wouldn't save too much.

another thing i saw, was that you were creating new Graphics Object every time, i've heard that it's quite slow. you should have global graphics object instead.

"SMART PAINTING"

http://java.sun.com/products/jfc/tsc/articles/painting/index.html#smart

you're spending most time while painting the gamefield. true?!

there is such method as .repaint(int, int, int, int), consider implementing that, instead of painting things over and over again, on the same place, you coluld check which segments neet to be changed (tip: the one where head moves to and the one from tail comes away from (when worm is not growing)), and then only repaint those segments.

sorry again for that not way too good exaple at the beginning of my reply, i hope noone will hold that against me.

VaskoLa at 2007-7-9 18:53:28 > top of Java-index,Other Topics,Java Game Development...
# 11

OK, I've done a little bit of testing and found no difference between creating a for loop with a pre created int or an int created when I start the loop.

I am obviously missing something here that I thought I knew, so can someone please point me in the direction of some documentation that will show me why this is the case?

Jacko

Pjackoa at 2007-7-9 18:53:28 > top of Java-index,Other Topics,Java Game Development...
# 12

> OK, I've done a little bit of testing and found no

> difference between creating a for loop with a pre

> created int or an int created when I start the loop.

Why should you?

> I am obviously missing something here that I thought I

> knew, so can someone please point me in the direction

> of some documentation that will show me why this is

> the case?

Umm...take it as widely believed fact. I really doubt anyone has written documents about that. At any rate, the difference is minimal and the compiler may optimize it somehow and JIT may optimize it.

Kayamana at 2007-7-9 18:53:28 > top of Java-index,Other Topics,Java Game Development...
# 13

> OK, I've done a little bit of testing and found no

> difference between creating a for loop with a pre

> created int or an int created when I start the loop.

>

> I am obviously missing something here that I thought I

> knew, so can someone please point me in the direction

> of some documentation that will show me why this is

> the case?

>

> Jacko

>

for(int i = 0;i < something;i++)

{

for(int j = 0;i j <somethingelse;j++)

{

}

}

the innerloop has its counter created everytime the outerloop loops.

int j;

for(int i = 0;i >< something;i++)

{

for(j = 0;i j <somethingelse;j++)

{

}

}

int j is only created once.

Its a micro-optimisation, and as such cannot be guaranteed to be faster

and as Kayaman pointed out, it may get optimised out automatically

by the JIT or compiler.

Even if it isn't optimised out, the difference will still be negligable, so it realy isn't worth doing.>

Abusea at 2007-7-9 18:53:28 > top of Java-index,Other Topics,Java Game Development...
# 14

a good 6 months later, have had another look at the source and changed quite a bit, and with the newer JVMs performance is more acceptable. I've set the framerate to be ~35fps so that you don't notice when it starts slowing down when the snake gets longer...

suggestions/comments welcome

asjf

ps. this is all one source file JAnaconda.javaimport javax.swing.*;

import java.awt.*;

import java.awt.event.*;

import java.awt.geom.*;

import java.awt.image.*;

import java.util.*;

import java.util.List;

public class JAnaconda extends JComponent implements Runnable {

public static final int WX = 480, WY = 380, BD = 19, MENU = 3, IN_GAME = 1, DEAD = 2;

GraphicsConfiguration defaultConfiguration;

Graphics2D onGraphics, offGraphics;

BufferedImage offScreen;

FontMetrics scFm;

Dimension dim = new Dimension(WX,WY);

Snake snake;

List food;

long state, score, timer, fps=36;

boolean pause, snakeMove;

double aaa;

public void run() {

defaultConfiguration = getGraphicsConfiguration();

offScreen = defaultConfiguration.createCompatibleImage(WX,WY);

offGraphics = offScreen.createGraphics();

offGraphics.setFont(new Font("Verdana",Font.BOLD,9));

onGraphics = (Graphics2D) getGraphics();

scFm = offGraphics.getFontMetrics(offGraphics.getFont());

snakeMove =true;

addKeyListener(new KeyAdapter() {

public void keyPressed(KeyEvent e) {

if(e.getKeyCode()==KeyEvent.VK_SPACE) {

if(state==MENU) state = IN_GAME; else pause=!pause;

}

}

} );

try {

while(true) {

long delay=0, frame=0;

snake = new Snake();

food = new ArrayList();

state = MENU;

timer = -(System.currentTimeMillis()+8000);

while(state!=IN_GAME) {

render(offGraphics);

onGraphics.drawImage(offScreen,0,0,null);

}

pause = false;

state = IN_GAME;

score = 0;

while(state!=DEAD) {

frame++;

timer = System.currentTimeMillis();

render(offGraphics);

onGraphics.drawImage(offScreen,0,0,null);

if(!pause) {

if(snakeMove)

snake.move();

for(int i=0; i<food.size(); i++)

((Food) food.get(i)).move();

List shapes = snake.getShapes();

if(shapes!=null && shapes.size()>0) {

Shape head = (Shape) shapes.get(0);

for(int j=0; j<food.size(); j++) {

Food f = (Food) food.get(j);

if( head.contains(f.x-3,f.y-3) || head.contains(f.x-3,f.y+3)

|| head.contains(f.x+3,f.y-3) || head.contains(f.x+3,f.y+3) ) {

food.remove(f);

snake.size*=(1.1+.05*f.type);

score += (snake.size/10)*(5+8*f.type);

}

}

for(int j=3; j><shapes.size(); j++)

if(head.getBounds().intersects(((Shape) shapes.get(j)).getBounds()))

state = DEAD;

}

if(snake.x >< BD || snake.x > WX-BD || snake.y < BD || snake.y > WY-BD)

state = DEAD;

for(int j =0; j < shapes.size(); j++){

Shape sh = (Shape) shapes.get(j);

for(int k=0; k<food.size(); k++) {

Food f = (Food) food.get(k);

if( sh.contains(f.x-3,f.y-3) || sh.contains(f.x-3,f.y+3)

|| sh.contains(f.x+3,f.y-3) || sh.contains(f.x+3,f.y+3) )

f.bounce(((Double)(snake.angles.get(j))).doubleValue());

}

}

if(Math.random()>0.99) {

food.add(new Food());

if(food.size()==10)

food.remove(0);

}

}

if((delay = (1000/fps)-(System.currentTimeMillis()-timer)) > 0)

Thread.sleep(delay);

}

long start = System.currentTimeMillis();

while((System.currentTimeMillis()-start) < 1500) {

render(offGraphics);

onGraphics.drawImage(offScreen,0,0,null);

}

}

} catch(Exception e) {

e.printStackTrace();

}

}

public void render(Graphics2D _gr) {

_gr.setColor(Color.black);

_gr.fillRect(0,0,WX,WY);

_gr.setColor(Color.white);

_gr.drawRect(BD,BD,WX-BD*2,WY-BD*2);

offGraphics.drawString("Anaconda",(WX/2)-30,15);

if(state!=MENU) {

offGraphics.drawString(""+score,20,15);

for(int i=0; i<food.size(); i++)

((Food) food.get(i)).paint(_gr);

snake.paint(_gr);

} else {

if((System.currentTimeMillis()/500)%2==0)

offGraphics.drawString("press space",BD+BD/2,2*BD);

offGraphics.setClip(WX/4, WY/14, 2*WX/4, 12*WY/14);

int h = scFm.getHeight(), a = scFm.getAscent();

int elapsed = (int)(timer+System.currentTimeMillis()), eoff =0;

if(elapsed>2000) {

if(elapsed<6000) {

eoff = -(11*20*(elapsed-2000))/4000;

} else if(elapsed<8000) {

eoff = -11*20;

} else if(elapsed<12000) {

eoff = -11*20 + (11*20*(elapsed-8000))/4000;

} else if(elapsed>20000) {

timer = -System.currentTimeMillis();

}

}

for(int i=0; i<25; i++) {

aaa+=0.0005D;

String s1 = "anonymous";

String s2 = ""+ (32000 / 25)*(25-i);

int s1w = scFm.stringWidth(s1), s2w = scFm.stringWidth(s2);

int toff = (int)(140+Math.sin(aaa+(i*16))*25+Math.abs(Math.cos(aaa/4+(i*16))*30));

offGraphics.setColor(new Color(toff,toff+60,toff));

int offset = i*20 + eoff;

offGraphics.drawString(s1, (WX-s1w) / 2 - 30, a + 2*WY/14 + offset);

offGraphics.drawString(s2, (WX-s2w) / 2 + 70, a + 2*WY/14 + offset);

}

offGraphics.setClip(null);

}

}

public Dimension getPreferredSize() {return dim;}

public boolean isFocusable(){return true;}

class Snake {

int jump = 6, size=75;

double x, y ,angle, dangle, ddangle;

double wid = 3.8, speed = 1.5, magic = jump<<3;

List ps, shapes, angles;

Color color;

Snake() {

angle = Math.PI;

ps = new ArrayList(10000);

this.x=JAnaconda.WX / 2;

this.y=JAnaconda.WY / 2;

addKeyListener(new KeyAdapter(){

boolean leftDown, rightDown;

public void keyPressed(KeyEvent e) {

leftDown |= e.getKeyCode()==KeyEvent.VK_LEFT;

rightDown |= e.getKeyCode()==KeyEvent.VK_RIGHT;

ddangle = leftDown==rightDown ? 0 : Math.PI/360 * (leftDown ? 1 : -1);

}

public void keyReleased(KeyEvent e) {

leftDown &= e.getKeyCode()!=KeyEvent.VK_LEFT;

rightDown &= e.getKeyCode()!=KeyEvent.VK_RIGHT;

ddangle = leftDown==rightDown ? 0 : Math.PI/360 * (leftDown ? 1 : -1);

}

} );

}

public List getShapes(){return shapes;}

void move() {

dangle = (dangle + ddangle) * 0.9D;

angle+=dangle;

angle += (angle<0 ? Math.PI*2 : (angle>Math.PI*2 ? -Math.PI*2 : 0));

x+= Math.sin(angle)*speed;

y+= Math.cos(angle)*speed;

ps.add(new Point2D.Double(x,y));

if(ps.size()==size) ps.remove(0);

shapes = new ArrayList(ps.size()/jump);

angles = new ArrayList(ps.size()/jump);

if(ps.size()>0) {

Point2D.Double p = (Point2D.Double) ps.get(ps.size()-1);

Point2D.Double pl = new Point2D.Double(p.x + Math.cos(angle)*wid*0.6, p.y - Math.sin(angle)*wid*0.6);

Point2D.Double pr = new Point2D.Double(p.x - Math.cos(angle)*wid*0.6, p.y + Math.sin(angle)*wid*0.6);

Point2D.Double diff = new Point2D.Double(), ql = new Point2D.Double(), qr = new Point2D.Double();

for(int i = ps.size()-1-jump; i>=jump; i=(i-jump)>=0 ? i-jump : (i==0 ? -1 : 0)) {

Point2D.Double q = (Point2D.Double) ps.get(i);

double d = p.distance(q) / (i<magic ? 1-Math.pow((magic-i)/magic,i/(jump><<1)) : 1);

if(i == ps.size()-1-jump) {d/=1.6;}

diff.setLocation( wid*(p.x-q.x)/d , wid*(p.y-q.y)/d);

ql.setLocation(q.x + diff.y, q.y-diff.x);

qr.setLocation(q.x - diff.y, q.y+diff.x);

Polygon s = new Polygon();

s.addPoint((int)pr.x,(int)pr.y); s.addPoint((int)pl.x,(int)pl.y);

s.addPoint((int)ql.x,(int)ql.y); s.addPoint((int)qr.x,(int)qr.y);

shapes.add(s);

angles.add(new Double(Math.atan(diff.x/diff.y) + ((diff.x>0?0:2)+(diff.x>0^diff.y>0?2:0))*Math.PI/2));

p = q;

pl.setLocation(ql.x,ql.y);

pr.setLocation(qr.x,qr.y);

}

}

}

void paint(Graphics2D g) {

double ca=0, ca2=Math.PI;

if(shapes!=null) {

for(Iterator i = shapes.iterator(); i.hasNext(); ) {

ca+=0.5D;

ca2+=0.75D;

g.setColor(new Color((int)(100 + Math.sin(ca)*30), (int)(200+Math.cos(ca)*40), (int)(100+Math.abs(Math.sin(ca2)))));

g.fill((Shape)i.next());

}

}

}

}

public static void main(String [] arg) throws Exception {

JFrame frame = new JFrame("JAnaconda");

JAnaconda j = new JAnaconda();

frame.getContentPane().add(j);

frame.pack();

frame.show();

new Thread(j).start();

}

}

class Food {

static Color stat = new Color(255,190,190), slow = new Color(200,255,200);

static Color fast = new Color(220,220,255);

static int count = 0;

int type;

double x, y, angle, speed;

public Food() {

angle = Math.random() * Math.PI * 2;

type = (count++>4 && Math.random()>0.7) ? ((Math.random()>0.75) ? 2 : 1) : 0;

speed = (type == 0 ? 0 : ((type == 1) ? 0.8 : 1.4));

x = JAnaconda.BD*2 + (Math.random() * (JAnaconda.WX-JAnaconda.BD*4));

y = JAnaconda.BD*2 + (Math.random() * (JAnaconda.WY-JAnaconda.BD*4));

}

public void move() {

x += Math.sin(angle) * speed;

y += Math.cos(angle) * speed;

if(x-3 < JAnaconda.BD || x+3 > JAnaconda.WX-JAnaconda.BD) bounce(Math.PI);

if(y-3 < JAnaconda.BD || y+3 > JAnaconda.WY-JAnaconda.BD) bounce(Math.PI/2);

}

public void bounce(double alpha) {

angle = (2*alpha - angle);

if(Double.isNaN(angle))

angle = Math.random() * Math.PI * 2;

angle += (angle<0 ? Math.PI*2 : (angle>Math.PI*2 ? -Math.PI*2 : 0));

}

public void paint(Graphics g){

g.setColor((type == 0 ? stat : ((type == 1) ? slow : fast)));

g.drawLine((int)(x-3),(int)(y-3),(int)(x+3),(int)(y+3));

g.drawLine((int)(x+3),(int)(y-3),(int)(x-3),(int)(y+3));

}

}

asjfa at 2007-7-9 18:53:28 > top of Java-index,Other Topics,Java Game Development...