Nice tool for compression here created by Riven great blog!
4KGamesDesign Wiki a bit old and with some dated information.
import java.applet.Applet;
import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
public class G extends Applet implements Runnable {
public void start() {
enableEvents(AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK |
AWTEvent.MOUSE_MOTION_EVENT_MASK);
new Thread(this).start();
}
public void run() {
setSize(800, 600); // For AppletViewer, remove later.
// Set up the graphics stuff, double-buffering.
BufferedImage screen = new BufferedImage(800, 600, BufferedImage.TYPE_INT_RGB);
Graphics g = screen.getGraphics();
Graphics appletGraphics = getGraphics();
// Some variables to use for the fps.
int tick = 0, fps = 0, acc = 0;
long lastTime = System.nanoTime();
// Game loop.
while (true) {
long now = System.nanoTime();
acc += now - lastTime;
tick++;
if (acc >= 1000000000L) {
acc -= 1000000000L;
fps = tick;
tick = 0;
}
// Update
// TODO add some update logic here.
lastTime = now;
// Render
g.setColor(Color.black);
g.fillRect(0, 0, 800, 600);
g.setColor(Color.white);
g.drawString("FPS " + String.valueOf(fps), 20, 30);
// Draw the entire results on the screen.
appletGraphics.drawImage(screen, 0, 0, null);
do {
Thread.yield();
} while (System.nanoTime() - lastTime < 0);
if (!isActive()) {
return;
}
}
}
public void processEvent(AWTEvent e) {
// Process the key event.
}
}
import java.applet.Applet;
import java.awt.*;
import java.awt.image.*;
/**
* Testbed for the basics.
*/
public class Skeleton extends Applet implements Runnable
{
// Constants.
// Must be at least 1026 if using Java 1.0 event model or someone accidentally
// hitting Ins will get AIOOBE. Pick a number used elsewhere and reuse it to
// save a constant pool entry.
private static final int NUM_KEYS = 1026;
private static final int WIDTH = 640;
private static final int HEIGHT = 480;
// Shared state. In the spirit of lock-free synchronisation, this is written
// only by the event thread, and it advances values rather than setting /
// resetting. This ensures that quick key presses aren't lost.
private int[] keyEventCount = new int[NUM_KEYS];
@Override
public void start()
{
new Thread(this).start();
}
public void run()
{
// Set up event handling. Note: keyDown[key] and keyPressed[key] are 0
// for false; any other value should be interpreted as true.
int[] prevKeyEventCounts = new int[NUM_KEYS];
int[] keyDown = new int[NUM_KEYS];
int[] keyPressed = new int[NUM_KEYS];
// Game state.
int x = 320;
int y = 320;
// Set up backbuffer for rendering.
BufferedImage buf = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
// Uncomment if you want to set pixels directly.
//int[] ibuf = ((DataBufferInt)buf.getRaster().getDataBuffer()).getData();
while (!isActive()) /* Busy-wait */;
while (isActive())
{
try
{
long now = System.currentTimeMillis();
// Events.
for (int i = 0; i < NUM_KEYS; i++)
{
int eventCount = keyEventCount[i];
// An odd number of events means that it's down. Simple!
// NB If you're worried about key repeat you might want
// instead to say
// keyDown[i] = (delta >> 1) + (eventCount & 1);
keyDown[i] = eventCount & 1;
// The key was pressed since the last loop if the number of events
// since then is at least two, by the pigeonhole principle, or if
// the number of events since then is positive and the key is down.
// Since we only need to catch the second case when delta == 1,
// we can simplify the key-is-down test a bit.
int delta = eventCount - prevKeyEventCounts[i];
keyPressed[i] = (delta >> 1) + (delta & eventCount);
prevKeyEventCounts[i] = eventCount;
}
// Logic.
if (keyDown[Event.LEFT] != 0) x--;
if (keyDown[Event.RIGHT] != 0) x++;
if (keyDown[Event.UP] != 0) y--;
if (keyDown[Event.DOWN] != 0) y++;
// Render to backbuffer.
Graphics g = buf.getGraphics();
g.setColor(new Color(0x000000));
g.fillRect(0, 0, WIDTH, HEIGHT);
g.setColor(new Color(0xffffff));
g.drawString("Score", x, y);
// Render backbuffer to front buffer and tidy.
Graphics gapp = getGraphics();
if (gapp != null)
{
gapp.drawImage(buf, 0, 0, this);
gapp.dispose();
}
g.dispose();
// Sleep. There are various ways of doing this, but IME you need to
// sleep rather than yield under Linux.
long t = System.currentTimeMillis();
long sleep = 20 - (t - now);
now = t;
if (sleep > 0)
{
Thread.sleep(sleep);
}
}
catch (Throwable ie)
{
}
}
}
@Override
public boolean handleEvent(Event evt)
{
// Non-key events have 0 for their key field.
keyEventCount[evt.key]++;
return false;
}
}
Play effects and music: code example from angryoctopus
/*
* S.java
*
* Created on 14 April 2009, 14:34
*/
/**
*
* @author Alan Waddington
*/
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.Clip;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import javax.swing.JFrame;
public final class S extends JFrame {
private final static int SCREENHEIGHT = 480; // Screen height
private final static int SCREENWIDTH = 640; // Screen width
private final static float RATE = 16000f;// Music sample rate
private final static float AMPLITUDE = 4000f; // Music amplitude
private byte musicLoop[]; // Array of 16 bit music sound samples
private Clip music; // Clip to play the above music track
int maxNote = 261;
int length = 512;
int tempo = 200;
String notes = "2gK8 I9k&+.y{^10[.p0l\"V^#pvoM$+#N8 I9k&+.y{^1%%bo0h2#^#Y4L8 \\4L8 \\4L8 Y4L8 f1M&$}xV^#g\"V^#j\"V^#QTd^#57@^#g\"V^#*55N$z=Y_1O(// aVU^#Nd1^#1Gl]#Pau7*g\"V^#l\"V^#Qt]]#DTd^#g\"V^#l\"V^#&oj%$M.A^#NTd^#57@^#Nd1^#1Gl]#Jd1^#1Gl]#,Gl]#77@^#57@^#3i481}xV^#l\"V^#Qt]]#KTd^#g\"V^#l\"V^#27@^#h2#^#c2#^#h2#^#Jt]]#6tx%$o+M8 V4Y_ o+M8 V4Y_ d[2^#1Gl]#e2#^#Jt]]#?i3t$Fw%^#8-0&$`k^]#,W9]#k)H]#ZDx7 <T}6*r_/&$v'}6*r_/&$svP7*h2#^#qxz]#1Gl]#@DQ7*h2#^#h2#^#c2#^#Jt]]#p)H]#F%+]#+/U%$P}T7 :'T7 4'a^ P}T7 :'T7 :'T7 :'T7 :'T7 :'T7 F%+]#A%+]#{W*]#uVT%$lKF7 4'a^ f+g'!)#N8 Jt]]#,W9]#MyDL$V2<6*{W*]#PR7%$*e;6*{W*]#{W*]#{W*]#{W*]#{W*]#_*9]#n9t\\#:'T7 VTE7 4'a^ lKF7 4'a^ f+g'!)#N8 Jt]]#,W9]#%U ";
/** Creates a new instance of S */
public S() {
// Create array of music frequencies
double[] freq = new double[100];
double f = 16.351562; // Frequency of C0 (C4 is middle C)
for (int i = 0; i < 7 * 12; i++) {
freq[i] = f;
f *= 1.0594630944; // Evenly tempered scale (12th root 2)
}
musicLoop = new byte[(int) (length * 30f / tempo * RATE)];
// Read each note and render it into the loop
int semiQuaver = 0; // Start time for next note
int byte0, byte1, twoNotes = 0;
for (int i = 0; i < maxNote; i++) {
if ((i & 1) == 0) {
// Decode 5 chars to 1 int
twoNotes = 0;
for (int j = 4; j >= 0; j--)
twoNotes = twoNotes * 95 + (int) notes.charAt(5 * (i >> 1) + j) - 0x20;
}
byte0 = twoNotes & 0xff; // Frequency
byte1 = twoNotes >> 8 & 0xff; // Delay & Duration
twoNotes = twoNotes >> 16;
double frequency = freq[byte0 & 0x7f];
if (byte0 > 127) // If bit7 is set, there is a delay before note
semiQuaver += (byte1 >> 4 & 0x0f) + 1; // Note start time
int duration = (byte1 & 0x0f) + 1; // Note duration
// Calculate start and stop times in terms of samples
int start = (int) ((semiQuaver) * 15f / tempo * RATE);
int stop = (int) ((semiQuaver + duration) * 15f / tempo * RATE);
// Calculate each sample and render it into the music loop
for (int j = start; j < stop; j++) {
double time = (j - start) / RATE; // Time (seconds)
// Triangle fundamental + sinusoidal 4th and 6th harmonics
double sin4 = (Math.sin(Math.PI * frequency * time * 8.0));
double sin6 = (Math.sin(Math.PI * frequency * time * 12.0));
double triangle = 2.0 * ((frequency * time) % 1.0);
if (triangle > 1.0)
triangle = 2.0 - triangle;
triangle = 2.0 * triangle - 1.0;
double sample = 0.5 * triangle + 0.2 * sin4 + 0.2 * sin6;
// Amplitude modulate the lot at about 2Hz
sample *= 0.6 + 0.4 * (time % 0.5) / 0.5;
// Apply attack and decay to avoid clicks
if (j - start < 1000)
sample *= (j - start) / 1000f;
if (stop - j < 1000)
sample *= (stop - j) / 1000f;
// Add the 16 bit sample to loop
int data = (musicLoop[2 * j] << 8) + (musicLoop[2 * j + 1] & 0xff);
data += AMPLITUDE * sample;
musicLoop[2 * j + 1] = (byte) (data & 0xff);
musicLoop[2 * j] = (byte) ((data >> 8) & 0xff);
}
}
try {
// Initialise Sound System
AudioFormat audioFormat = new AudioFormat(RATE, 16, 1, true, true);
DataLine.Info info = new DataLine.Info(Clip.class, audioFormat);
music = (Clip) AudioSystem.getLine(info);
music.open(audioFormat, musicLoop, 0, musicLoop.length);
music.loop(Clip.LOOP_CONTINUOUSLY);
} catch (Exception e) {
e.printStackTrace(); // Display error, but keep going
}
// Screen
BufferedImage screen = new BufferedImage(SCREENWIDTH, SCREENHEIGHT, BufferedImage.TYPE_INT_RGB);
int[] screenData = ((DataBufferInt) screen.getRaster().getDataBuffer()).getData();
Graphics g;
setSize(SCREENWIDTH, SCREENHEIGHT);
show();
while (isVisible()) {
int position = music.getFramePosition() % musicLoop.length;
for (int x = 0; x < SCREENWIDTH * 20; x++) {
int xx = x / 20;
int p = (2 * position + 2 * x) % musicLoop.length;
int note = 256 * musicLoop[p] + (musicLoop[p + 1] & 0xff);
for (int y = 0; y < SCREENHEIGHT; y++) {
screenData[xx + y * SCREENWIDTH] = 0;
}
int yy = SCREENHEIGHT / 2 + note / 32;
if (yy < 0)
yy = 0;
if (yy >= SCREENHEIGHT)
yy = SCREENHEIGHT - 1;
screenData[xx + yy * SCREENWIDTH] = 0xffffff;
}
g = getContentPane().getGraphics();
g.drawImage(screen, 0, 0, null);
g.dispose();
}
}
/**
* @param args
* the command line arguments
*/
public static void main(String[] args) {
new S();
System.exit(0);
}
}