//******************************************************************************** // Kaeroid.java // // "FROG QUEUE"(カエロイド) // // (c) 1997 Maeda Mameo http://www.alles.or.jp/~mameo/ //******************************************************************************** import java.awt.*; public class Kaeroid extends java.applet.Applet implements Runnable{ private Image imageOff, imageStatics; private Graphics gOff, gStatics; private FontMetrics theFontMetrics; private int appW, appH; private Thread theThread; private boolean paintRequest; static final double PI = Math.PI; static final double PI2 = 2 * PI; static final double FITDISTANCE = 47; static final double MAXSPEED = 14; static final double FLOCKWEIGHT = .06; static final double CENTERWEIGHT = .007; static final double TARGETWEIGHT = .1; static final double MIMICWEIGHT = .1; static final double ACCELERATION = 1.3; static final double VISIBLEANGLE = .9 * PI; static final int MAXFROGNUM = 30; private int frogNum; private double frogX[] = new double[MAXFROGNUM]; private double frogY[] = new double[MAXFROGNUM]; private double frogAngle[] = new double[MAXFROGNUM]; private double frogSpeed[] = new double[MAXFROGNUM]; private int frogTarget[] = new int[MAXFROGNUM]; private int frogEscape[] = new int[MAXFROGNUM]; private double frogEscapeAngle[] = new double[MAXFROGNUM]; private int mouseX, mouseY; private int clickedX, clickedY; private int frogR, frogG, frogB; private Color frogColor; private int markedFrog; private double magnification; private boolean fpsRequest; private String fpsString; static final int MAXFPS = 20; //******************************************************************************** //******************************************************************************** public void init(){ Dimension d; fpsString = ""; paintRequest = fpsRequest = false; setBackground(Color.white); d = size(); appW = d.width; appH = d.height; imageOff = createImage( appW, appH); gOff = imageOff.getGraphics(); imageStatics = createImage( appW, appH); gStatics = imageStatics.getGraphics(); gStatics.setFont( new Font( "TimesRoman", java.awt.Font.BOLD, 300)); theFontMetrics = gStatics.getFontMetrics(); } public void update( Graphics g){ paint(g);} public void paint( Graphics g){ if(paintRequest){ if(fpsRequest) showStatus(fpsString); g.drawImage( imageOff, 0, 0, this); paintRequest = false; } } public void start(){ theThread = new Thread(this); theThread.start();} public void stop(){ theThread.stop(); theThread = null;} public boolean mouseDown( Event e, int x, int y){ if( clickedY < 0){ clickedX = x; clickedY = y; } return true; } public boolean mouseMove( Event e, int x, int y){ mouseX = x; mouseY = y; return true; } public boolean mouseEnter( Event e, int x, int y){ fpsRequest = true; return true; } public boolean mouseExit( Event e, int x, int y){ fpsRequest = false; showStatus(null); return true; } //******************************************************************************** //******************************************************************************** public void run(){ int i, r, g, b; String s; int timeMillisCount; long delay, averageTime, latestTimeMillis, t; timeMillisCount = 0; delay = 20; latestTimeMillis = System.currentTimeMillis(); frogNum = MAXFROGNUM; clickedY = 0; while(true){ if( clickedY >= 0){ if( frogNum < MAXFROGNUM){ frogX[frogNum] = clickedX; frogY[frogNum] = clickedY; frogAngle[frogNum] = PI2 * Math.random(); frogSpeed[frogNum] = MAXSPEED; frogTarget[frogNum] = -1; frogEscape[frogNum] = 0; markedFrog = frogNum; frogNum++; } else resetFrog(); gStatics.setColor(Color.white); gStatics.fillRect( 0, 0, appW, appH); s = Integer.toString(frogNum); gStatics.setColor(Color.lightGray); gStatics.drawString( s, ( appW - theFontMetrics.stringWidth(s)) / 2, ( appH + theFontMetrics.getAscent()) / 2); for( i = 0; i < appH; i += 3) gStatics.drawLine( 0, i, appW - 1, i); gStatics.setColor(Color.white); for( i = 1; i < appH; i += 3) gStatics.drawLine( 0, i, appW - 1, i); clickedY = -1; } moveFrog(); sleeper(delay); while(paintRequest) sleeper(1); gOff.drawImage( imageStatics, 0, 0, this); if( markedFrog >= 0){ gOff.setColor(Color.white); gOff.fillOval( (int)frogX[markedFrog] - 30, (int)frogY[markedFrog] - 30, 60, 60); } drawConnection(); drawFrog(); paintRequest = true; repaint(); if( ++timeMillisCount == 20){ t = System.currentTimeMillis(); averageTime = ( t - latestTimeMillis) / 20; fpsString = "" + 1000 / averageTime + "." + 10000 / averageTime % 10 + " frames a second."; delay = Math.max( 20, delay + 1000 / MAXFPS - averageTime); latestTimeMillis = t; timeMillisCount = 0; } } } private void resetFrog(){ int i; double a; frogNum = 7; markedFrog = -1; for( i = 0; i < frogNum; i++){ a = PI2 * i / frogNum; frogX[i] = ( .5 + .1 * Math.cos(a)) * appW; frogY[i] = ( .5 + .1 * Math.sin(a)) * appH; frogAngle[i] = a + .5 * PI; frogSpeed[i] = .3 * MAXSPEED; frogTarget[i] = -1; frogEscape[i] = 0; } } private void drawConnection(){ int i, t, x, y; gOff.setColor(Color.gray); for( i = 0; i < frogNum; i++){ t = frogTarget[i]; if( t >= 0){ gOff.drawLine( (int)frogX[i], (int)frogY[i], (int)frogX[t], (int)frogY[t]); } } } private void drawFrog(){ int i, r, g, b; double a, x, y; frogR = ( frogR + 11) % 400; if( frogR < 200) r = 20 + frogR; else r = 419 - frogR; frogG = ( frogG + 15) % 400; if( frogG < 200) g = 56 + frogG; else g = 455 - frogG; frogB = ( frogB + 19) % 400; if( frogB < 200) b = 20 + frogB; else b = 419 - frogB; frogColor = new Color( r, g, b); for( i = 0; i < frogNum; i++){ x = frogX[i]; y = frogY[i]; a = frogAngle[i]; if( frogTarget[i] >= 0){ magnification = .25; drawOneFrog( x, y, frogColor, a); } else{ magnification = .33; drawOneFrog( x, y, Color.red, a); } } } private void drawOneFrog( double x, double y, Color c, double r){ double xL, xR, vx, yL, yR, vy; vx = magnification * Math.cos(r); vy = magnification * Math.sin(r); xL = x + 28 * vx + 32 * vy ; yL = y + 28 * vy - 32 * vx; xR = x + 28 * vx - 32 * vy ; yR = y + 28 * vy + 32 * vx; gOff.setColor(Color.black); drawFillCircle( x, y, 40); drawFillCircle( xL, yL, 24); drawFillCircle( xR, yR, 24); gOff.setColor(c); drawFillCircle( x, y, 32); drawFillCircle( xL, yL, 16); drawFillCircle( xR, yR, 16); gOff.setColor(Color.white); drawFillCircle( xL, yL, 12); drawFillCircle( xR, yR, 12); gOff.setColor(Color.black); drawFillCircle( xL + 8 * vx, yL + 8 * vy, 7); drawFillCircle( xR + 8 * vx, yR + 8 * vy, 7); drawFillCircle( x - 5 * vx + 8 * vy, y - 5 * vy - 8 * vx, 3); drawFillCircle( x - 5 * vx - 8 * vy, y - 5 * vy + 8 * vx, 3); } private void drawFillCircle( double x, double y, int r){ gOff.fillOval( (int)( x - magnification * r), (int)( y - magnification * r), (int)( magnification * 2 * r) + 1, (int)( magnification * 2 * r) + 1 ); } private void moveFrog(){ int i, t; double a, s, x, y, cx, cy, vx, vy; for( i = 0; i < frogNum; i++){ x = frogX[i]; y = frogY[i]; a = frogAngle[i]; s = frogSpeed[i]; x += Math.cos(a) * s; y += Math.sin(a) * s; if( x < 0) x += appW; else if( x >= appW) x -= appW; if( y < 0) y += appH; else if( y >= appH) y -= appH; frogX[i] = x; frogY[i] = y; frogAngle[i] = a; if( fpsRequest && Math.pow( x - mouseX, 2) + Math.pow( y - mouseY, 2) < 20 * 20) markedFrog = i; } cx = cy = 0; for( i = 0; i < frogNum; i++){ cx += frogX[i]; cy += frogY[i]; } cx /= frogNum; cy /= frogNum; for( i = 0 ; i < frogNum; i++){ x = frogX[i]; y = frogY[i]; a = frogAngle[i]; s = frogSpeed[i]; t = frogTarget[i]; if( frogEscape[i] == 0){ t = findTarget(i); if( t < 0){ a += FLOCKWEIGHT * angleDifference( getAngle( cx - x, cy - y), a); s = ( 1 - .7 * Math.random()) * MAXSPEED; } else{ vx = frogX[t] - x; vy = frogY[t] - y; a += CENTERWEIGHT * angleDifference( getAngle( appW / 2 - x, appH / 2 - y), a); a += TARGETWEIGHT * angleDifference( getAngle( vx, vy), a); a += MIMICWEIGHT * angleDifference( frogAngle[t], a); if( vx * vx + vy * vy < FITDISTANCE * FITDISTANCE) s = 3 + 7 * Math.random(); else s *= ACCELERATION; if( s > MAXSPEED) s = MAXSPEED; else if( s < 1) s = 1; if( Math.random() < .002){ //********** Someone occasionally escapes from the queue. s = MAXSPEED; if( t == 0) frogEscapeAngle[i] = 0; else if( angleDifference( getAngle( vx, vy), a) < 0) frogEscapeAngle[i] = .1 * PI; else frogEscapeAngle[i] = -.07 * PI; t = -1; frogEscape[i] = 9; } } } else{ a += frogEscapeAngle[i]; frogEscape[i]--; } if( a < 0) a += PI2; else if( a >= PI2) a -= PI2; frogAngle[i] = a; frogSpeed[i] = s; frogTarget[i] = t; } } private int findTarget( int n){ //********** Finding the target to chase. int i, target; double a, b, d, x, y, vx, vy, targetD; targetD = Math.pow( .6 * appH, 2); target = -1; x = frogX[n]; y = frogY[n]; a = frogAngle[n]; for( i = 0; i < frogNum; i++) if( i != n){ vx = frogX[i] - x; vy = frogY[i] - y; d = vx * vx + vy * vy; if( d < targetD){ //********** The target must be in short distance. b = angleDifference( a, frogAngle[i]); if( b > -.6 * PI && b < .6 * PI){ //********** The direction of the target must be similar. b = angleDifference( getAngle( vx, vy), a); if( b > -.5 * VISIBLEANGLE && b < .5 * VISIBLEANGLE){ //********** The target must be in sight. targetD = d; target = i; } } } } return target; } private double getAngle( double vx, double vy){ double a; if( vy == 0) if( vx < 0) return PI; else return 0; a = Math.acos( vx / Math.sqrt( vx * vx + vy * vy)); if( vy > 0) return a; else return ( PI2 - a); } private double angleDifference( double t, double s){ double a; a = t - s; if( a > PI) return( a - PI2); if( a < -PI) return( a + PI2); return a; } private void sleeper( long t){ try theThread.sleep(t); catch( InterruptedException e);} }