Skip to content

Instantly share code, notes, and snippets.

@boxmein
Last active March 30, 2020 13:19
Show Gist options
  • Save boxmein/7618661 to your computer and use it in GitHub Desktop.
Save boxmein/7618661 to your computer and use it in GitHub Desktop.
Processing implementation of a small falling-sand game.
// Something relating to a falling sand game
//
// Usage:
//
// After opening the Processing IDE, copy it into an empty new sketch.
// Just as simple as that.
//
//
// Playing:
//
// Click to draw.
// Right-click to delete.
// Scroll up to enlarge the brush.
// Scroll down to make the brush smaller.
// The right arrow key goes to the "next" element ID.
// ( There are two elements: wall (1) and magic-lefty-powder (2) )
// The left arrow key goes to the "previous" element ID.
// Space is for pause.
// Written by boxmein in 2013.
// Use under the terms of the MIT license.
import java.util.List;
import java.util.ArrayList;
import java.util.Random;
//
// Classes and interfaces
//
// Element class for defining an element
interface Element {
int flags = 0;
int type = 0;
// Ticking method
// All reactions and probably more intricate
// movement should be performed in here
void Update (Particle p, int x, int y, List<Particle> particles, Particle[][] pmap);
// Drawing method
// Pass index, x, y
// Return colour in 0xRRGGBB
int Draw (Particle p, int x, int y, Particle[][] pmap);
}
// Particle class for all the particles on the field
class Particle {
int x, y, life, flags;
int type;
// Move a particle to a new place forcefully
void moveTo(int x, int y, Particle[][] pmap) {
if (x > 0 && x < width &&
y > 0 && y < height &&
pmap[x][y] == null) {
pmap[this.x][this.y] = null;
this.x = x;
this.y = y;
pmap[x][y] = this;
}
}
// Create a particle
public Particle (int x, int y, int type) {
this.x = x;
this.y = y;
this.type = type;
}
}
// Element implementations
public class Wall implements Element {
void Update (Particle p, int x, int y, List<Particle> parts, Particle[][] pmap) {
}
int Draw (Particle p, int x, int y, Particle[][] pmap) {
return 0xF7F7F7;
}
}
public class Sand implements Element {
Random rnd;
public Sand () {
rnd = new Random();
}
void Update (Particle p, int x, int y, List<Particle> ps, Particle[][] pmap) {
switch (rnd.nextInt(3)) {
case 0:
p.moveTo(x+1, y+1, pmap);
case 1:
p.moveTo(x, y+1, pmap);
case 2:
p.moveTo(x-1, y+1, pmap);
}
}
int Draw (Particle p, int x, int y, Particle[][] pmap) {
return 0xFFFED4;
}
}
//
// Constants
//
// Particle types that exist, two in this case.
static final int P_WALL = 1;
static final int P_SAND = 2;
static final int P_NUM = 3;
// all sorts of flags
// Movement flags: for no movement, don't add any of those
// For custom movement, don't add any of those :D
static final int FMOVE_POWDER = 0x01;
static final int FMOVE_LIQUID = 0x02;
//
// Non-constant state
//
boolean paused = false, drawing = false, deleting = false;
int colstate = 0, brushsize = 5, brushstate = P_WALL;
List<Particle> particles;
List<Element> elements;
Particle[][] pmap;
void setup () {
size(612, 384, P2D);
background(0);
noStroke();
fill(255, 0, 255);
particles = new ArrayList<Particle>();
elements = new ArrayList<Element>(P_NUM);
pmap = new Particle[width][height];
// add new elements here for cross-referencing
elements.add(0, null);
elements.add(P_WALL, new Wall());
elements.add(P_SAND, new Sand());
}
void draw () {
background(0);
// Brushing
if (drawing) {
for (int sy = (int) - Math.ceil(brushsize/2) ;
sy <= Math.floor(brushsize/2) ; sy++) {
for (int sx = (int) -Math.ceil(brushsize/2);
sx<=Math.floor(brushsize/2); sx++) {
if (mouseX+sx > 0 && mouseX+sx < width &&
mouseY+sy > 0 && mouseY+sy < height) {
Particle p = new Particle(mouseX+sx, mouseY+sy, brushstate);
particles.add(p);
// now the topmost particle in the pmap array
pmap[mouseX+sx][mouseY+sy] = p;
}
}
}
}
// Deleting
else if (deleting) {
for (int sy = (int) - Math.ceil(brushsize/2) ;
sy <= Math.floor(brushsize/2) ; sy++) {
for (int sx = (int) -Math.ceil(brushsize/2);
sx<=Math.floor(brushsize/2); sx++) {
if (mouseX+sx > 0 && mouseX+sx < width &&
mouseY+sy > 0 && mouseY+sy < height) {
if (pmap[mouseX+sx][mouseY+sy] == null) continue;
particles.remove(particles.indexOf(pmap[mouseX+sx][mouseY+sy]));
pmap[mouseX+sx][mouseY+sy] = null;
}
}
}
}
// Particle update
if (!paused && particles.size() > 0) {
for (Particle p : particles) {
if (p.type > 0) {
elements.get(p.type).Update(p, p.x, p.y, particles, pmap);
}
}
}
// Drawing
for (int y=0;y<height;y++) {
for (int x=0;x<width;x++) {
if (pmap[x][y] == null)
continue;
colstate = elements.get(pmap[x][y].type).Draw(pmap[x][y], x, y, pmap);
fill((colstate>>16)&0xFF, (colstate>>8)&0xFF, colstate&0xFF);
rect(x, y, 1, 1);
}
}
}
void keyPressed () {
if (key == CODED) {
switch (keyCode) {
case UP:
break;
case DOWN:
break;
case LEFT:
if (brushstate - 1 > 0)
brushstate--;
break;
case RIGHT:
if (brushstate + 1 < P_NUM)
brushstate++;
break;
default:
break;
}
}
else {
switch (key) {
case ' ':
paused = !paused;
break;
}
}
}
void mousePressed () {
if (mouseButton == LEFT)
drawing = true;
else if (mouseButton == RIGHT)
deleting = true;
}
void mouseReleased () {
if (mouseButton == LEFT)
drawing = false;
else if (mouseButton == RIGHT)
deleting = false;
}
void mouseWheel (MouseEvent evt) {
float e = evt.getAmount();
if (e < 0)
brushsize += 2;
else if (e > 0 && brushsize-2>1)
brushsize -= 2;
// Visual indicator of about how large the brush is.
stroke(170);
fill(0, 0, 0, 0);
rect(mouseX-(brushsize/2),
mouseY-(brushsize/2),
brushsize,brushsize);
noStroke();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment