Image Cursor in Tuio Code - processing

I'm looking to make an external image cursor to track the fiducial codes in tuio for my graffiti wall. Since I need background to not be run constantly in void draw(), the image cursor leaves a trail of the image around the stage. Anyone know how I can stop it from doing this without placing background in void draw()?
This is my code;
// background(255);
textFont(font, 18*scale_factor);
float obj_size = object_size*scale_factor;
float cur_size = cursor_size*scale_factor;
Vector tuioObjectList = tuioClient.getTuioObjects();
for (int i=0;i<tuioObjectList.size();i++) {
TuioObject tobj = (TuioObject)tuioObjectList.elementAt(i);
stroke(0);
fill(0);
//pushMatrix();
//translate(tobj.getScreenX(width),tobj.getScreenY(height));
//rotate(tobj.getAngle());
image(spray,10,10);
//rect(-obj_size/2,-obj_size/2,obj_size,obj_size);
// popMatrix();
if (mp == true)
{
if (tobj.getSymbolID()==66) {
fill(#FF00FF);
noStroke();
tint(255,127);
ellipse(tobj.getScreenX(width), tobj.getScreenY(height), 50, 50);
fill(#FF00FF);
text(""+tobj.getSymbolID(), tobj.getScreenX(width), tobj.getScreenY(height));
}
if (tobj.getSymbolID()==23) {
fill(#0000FF);
noStroke();
tint(255,127);
ellipse(tobj.getScreenX(width), tobj.getScreenY(height), 50, 50);
fill(#0000FF);
text(""+tobj.getSymbolID(), tobj.getScreenX(width), tobj.getScreenY(height));
}
if (tobj.getSymbolID()==22) {
fill(#00FF00);
noStroke();
tint(255,127);
ellipse(tobj.getScreenX(width), tobj.getScreenY(height), 50, 50);
fill(#00FF00);
text(""+tobj.getSymbolID(), tobj.getScreenX(width), tobj.getScreenY(height));
}
}
}
Vector tuioCursorList = tuioClient.getTuioCursors();
for (int i=0;i<tuioCursorList.size();i++) {
TuioCursor tcur = (TuioCursor)tuioCursorList.elementAt(i);
Vector pointList = tcur.getPath();
if (pointList.size()>0) {
stroke(0, 0, 255);
TuioPoint start_point = (TuioPoint)pointList.firstElement();
;
for (int j=0;j<pointList.size();j++) {
TuioPoint end_point = (TuioPoint)pointList.elementAt(j);
line(start_point.getScreenX(width), start_point.getScreenY(height), end_point.getScreenX(width), end_point.getScreenY(height));
start_point = end_point;
}
stroke(192, 192, 192);
fill(192, 192, 192);
ellipse( tcur.getScreenX(width), tcur.getScreenY(height), cur_size, cur_size);
fill(0);
text(""+ tcur.getCursorID(), tcur.getScreenX(width)-5, tcur.getScreenY(height)+5);
}
}
}
void mousePressed () {
bDrawFullSize = true;
mp = true;
player.play();
if (mouseX > x && mouseX < x+w && mouseY > y && mouseY < y+h) {
button = !button;
}
}
/*if(mousePressed) {
if(mouseX>x && mouseX <x+w && mouseY>y && mouseY <y+h){
image(stencil1,200,100,FULL_SIZE, FULL_SIZE);
}
else
{
if(mouseX>x && mouseX <x+w && mouseY>y && mouseY <y+h){
image(stencil2,200,100,FULL_SIZE, FULL_SIZE);
}
}
}
}*/
void mouseReleased() {
mp = false;
player.close();
//since close closes the file, we need to load the sound effect again.
player = minim.loadFile("spray_close.wav");
}
void mouseDragged() {
drag = true;
}
// these callback methods are called whenever a TUIO event occurs
// called when an object is added to the scene
void addTuioObject(TuioObject tobj) {
println("add object "+tobj.getSymbolID()+" ("+tobj.getSessionID()+") "+tobj.getX()+" "+tobj.getY()+" "+tobj.getAngle());
}
// called when an object is removed from the scene
void removeTuioObject(TuioObject tobj) {
println("remove object "+tobj.getSymbolID()+" ("+tobj.getSessionID()+")");
}
// called when an object is moved
void updateTuioObject (TuioObject tobj) {
println("update object "+tobj.getSymbolID()+" ("+tobj.getSessionID()+") "+tobj.getX()+" "+tobj.getY()+" "+tobj.getAngle()
+" "+tobj.getMotionSpeed()+" "+tobj.getRotationSpeed()+" "+tobj.getMotionAccel()+" "+tobj.getRotationAccel());
}
// called when a cursor is added to the scene
void addTuioCursor(TuioCursor tcur) {
println("add cursor "+tcur.getCursorID()+" ("+tcur.getSessionID()+ ") " +tcur.getX()+" "+tcur.getY());
}
// called when a cursor is moved
void updateTuioCursor (TuioCursor tcur) {
println("update cursor "+tcur.getCursorID()+" ("+tcur.getSessionID()+ ") " +tcur.getX()+" "+tcur.getY()
+" "+tcur.getMotionSpeed()+" "+tcur.getMotionAccel());
}
// called when a cursor is removed from the scene
void removeTuioCursor(TuioCursor tcur) {
println("remove cursor "+tcur.getCursorID()+" ("+tcur.getSessionID()+")");
}
// called after each message bundle
// representing the end of an image frame
void refresh(TuioTime bundleTime) {
redraw();
}
void keyPressed() {
endRecord();
background(bg1);
bDrawFullSize = false;
button = false;
}

Maybe using layers, draw your cursor and/or the other stuff in different layers. Erase one layer and not other as you need, display all them in draw. This is usually done with PGraphics objects. Search processing + layers to see samples. Here and/or in processing forum
something like this:
EDIT: Actually I think that the way to do it is to move all non refreshing draw to a PGraphics and do refresh draw with background(), where you can draw refreshing stuff without a trail. I changed the code to reflect that.
PGraphics l1;
void setup() {
size(200, 200);
l1 = createGraphics(200, 200, JAVA2D);
background(255);
noStroke();
}
void draw() {
background(255);
l1.beginDraw();
l1.fill(255, 0, 255);
l1.noStroke();
if (frameCount%100 == 0) {
l1.rect(random(width), random(height), 20, 20);
}
l1.endDraw();
image(l1, 0, 0);
fill(230);
ellipse(mouseX, mouseY, 30, 30);
}

Related

Processing Fix & Optimization of https://github.com/jonlit/spacestarprocessing3d

I previously had an issue with a model not loading correctly (see Processing - loading obj File)
https://stackoverflow.com/users/89766/george-profenza helped me solve the problem in chat, and he wanted to post his optimizations to my code publically.
This also solved the original problem described in the question mentioned above.
You can check out the game at https://github.com/jonlit/spacestarprocessing3d
As mentioned in chat there were a few things slightly off with the existing approach and for visiblity, this are the steps taken to address the issues.
Hope this helps other to debug Processing P3D / OBJ issues:
The first step was to identify the slowest pieces of code. This was done using VisualVM.
This highlighted shape() calls were slow (not not why):
Step 2 was to isolate the problem. Why is loading/displaying a couple of obj files slow.
For reference these are the assets:
rock.obj using rockTexture.png (but currently missing .mtl)
cirno_low.obj using cirno_low_u1_v1.jpeg
This is a test sketch loading/display the .obj files as they are:
PShape rock;
PShape cirno;
void setup(){
size(900, 900, P3D);
cirno = loadShape("cirno_low.obj");
rock = loadShape("rock.obj");
int faces = 0;
int vertices = 0;
for(int i = 0 ; i < rock.getChildCount(); i++){
PShape c = rock.getChild(i);
vertices += c.getVertexCount();
faces++;
}
println("rock faces", faces, "vertices", vertices);
}
void draw(){
background(0);
lights();
translate(width * 0.5, height * 0.5, 0);
rotateY(map(mouseX, 0, width, -PI, PI));
rotateX(map(mouseY, 0, height, PI, -PI));
for(int i = 0 ; i < 81; i++){
pushMatrix();
translate(i % 9 * 100 - width * 0.5,
i / 9 * 100 - height * 0.5, -100);
rotate(map(i, 0, 80, -PI, PI), 0.5, 0.5, 0);
scale(0.5);
shape(rock);
popMatrix();
}
pushMatrix();
scale(10);
shape(cirno);
popMatrix();
surface.setTitle((int)frameRate + "fps");
}
It renders pretty fast, without textures though:
The game uses setTexture() and interestingly enough this drops the frame rate:
PShape rock;
PShape cirno;
void setup(){
size(900, 900, P3D);
cirno = loadShape("cirno_low.obj");
cirno.setTexture(loadImage("cirno_low_u1_v1.jpeg"));
rock = loadShape("rock.obj");
rock.setTexture(loadImage("rockTexture.png"));
int faces = 0;
int vertices = 0;
for(int i = 0 ; i < rock.getChildCount(); i++){
PShape c = rock.getChild(i);
vertices += c.getVertexCount();
faces++;
}
println("rock faces", faces, "vertices", vertices);
}
void draw(){
background(0);
lights();
translate(width * 0.5, height * 0.5, 0);
rotateY(map(mouseX, 0, width, -PI, PI));
rotateX(map(mouseY, 0, height, PI, -PI));
for(int i = 0 ; i < 81; i++){
pushMatrix();
translate(i % 9 * 100 - width * 0.5,
i / 9 * 100 - height * 0.5, -100);
rotate(map(i, 0, 80, -PI, PI), 0.5, 0.5, 0);
scale(0.5);
shape(rock);
popMatrix();
}
pushMatrix();
scale(10);
shape(cirno);
popMatrix();
surface.setTitle((int)frameRate + "fps");
}
Without checking the PShape source code, the assumption is behind the scenes the PShape has to do more work behind the scenes, because loading an .obj file with an .mtl (which helps load the texture as well) render just fine.
Here's the Processing > Examples > Basics > Shape > LoadDisplayOBJ example tweaked: it renders 1250 instances at 60fps:
/**
* Load and Display an OBJ Shape.
*
* The loadShape() command is used to read simple SVG (Scalable Vector Graphics)
* files and OBJ (Object) files into a Processing sketch. This example loads an
* OBJ file of a rocket and displays it to the screen.
*/
PShape rocket;
float ry;
public void setup() {
size(900, 900, P3D);
rocket = loadShape("rocket.obj");
}
public void draw() {
background(0);
lights();
translate(width/2, height/2 + 100, -200);
rotateY(map(mouseX, 0, width, -PI, PI));
rotateX(map(mouseY, 0, height, PI, -PI));
int nc = 1250;
float nr = sqrt(nc);
float sp = 150;
for(int i = 0 ; i < nc; i++){
pushMatrix();
translate(i % nr * sp - width * 0.5,
i / nr * sp - height * 0.5, -sp);
//rotate(map(i, 0, 80, -PI, PI), 0.5, 0.5, 0);
rotateZ(PI + radians(i));
rotateY(ry);
scale(0.5);
shape(rocket);
popMatrix();
}
//rotateZ(PI);
//rotateY(ry);
//shape(rocket);
ry += 0.02;
surface.setTitle((int)frameRate + "fps");
}
This pointed out another issue with how the obj files were used in the game:
each new Star() for example would load the .obj again.
public class Star extends UFO {
public Star (int x, int y, int spd) {
posX = x;
posY = y;
rot = int(random(0, 360));
speed = spd;
symbol = loadShape("rock.obj");
symbol.setTexture(rockTexture); //<>//
...
Ideally these meshes would be loaded once in setup(), with .mtl files and references passed to each instance needing to render them via shape().
This would allow instancing to work as it's the same geometry rendered multiple times. Reloading the same obj file into new memory addresses for each instance would result in many duplicated resources.
One quick fix for the .mtl issue is to simply import the obj in Blender, select it, apply the texture and export it:
(This would also be a good opportunity to rotate/scale models so when they're loaded in Processing, no additional transforms are required and they all can live an in easy to understand coordinate system)
The contents of the exported files I've used are:
cirno_lowWithMTL.mtl
cirno_lowWithMTL.obj
cirno_lowWithMTLDecimated.mtl
cirno_lowWithMTLDecimated.obj
They load/display (with textures) at 60fps (due to the .mtl files)
The recommended optimisation steps (other than using .obj with .mtl files and loading once and re-using mulitple times) are:
avoid extending fixed length arrays (e.g. kryptonit = (Kryptonit[]) append(kryptonit, new Kryptonit(int(random(50, width-150)), int(random(-300, 0))));). ArrayLists are better suited for resizing. In this case in particular a fixed length array is great, as long as it's objects are pre-allocated once, then the positions / states of the objects are updated (e.g. outside of screen objects are marked for re-use and hidden and instead of new objects, existing objects have positions visiblity/reset): in other words Object Pooling)
if meshes are displayed from a single point of view with only rotation on Z axis and position affecting them, they could be images (sprites) instead. (e.g. exporting a static image with alpha channel from Blender at the right scale (or using PGraphics to do this at runtime))
once meshes are loaded, instead of using transformations on them in draw() (e.g. symbol.rotateX(value), which will affect every single vertex in the PShape, use pushMatrix()/popMatrix() call with shape() so simply render the same geometry with different tranformations.
For reference this is the full program with minimal tweaks around loading/using .obj files efficiently (with the old approach commented out and few notes around those regions):
import com.dhchoi.CountdownTimer;
import com.dhchoi.CountdownTimerService;
import controlP5.*;
int zeit;
int punkte;
int leben;
int schwierigkeit = 20;
int zustand = 1;
int boost = 0;
int highscore = 0;
int minuten = 0;
int changeLevel = 0;
boolean paused = true;
boolean gameOver;
JSONArray saves = new JSONArray();
PFont gameOverFont;
PFont gameOverFontSmall;
CountdownTimer timer1 = CountdownTimerService.getNewCountdownTimer(this).configure(1000, 60000);
CountdownTimer kryptonitAnimationTimer1 = CountdownTimerService.getNewCountdownTimer(this).configure(10, 250);
boolean[] keysPressed = new boolean[65536];
ControlP5 cp5;
Star[] stars;
Kryptonit[] kryptonit;
Raumschiff raumschiff;
Player[] players;
String textValue = "";
PImage cirnoTexture;
PImage rockTexture;
PShape cirno;
PShape rock;
void loadMeshes(){
rock = loadShape("rockWithMTL.obj");
rock.scale(0.2);
cirno = loadShape("cirno_lowWithMTL.obj");
cirno.rotateY(HALF_PI);
cirno.rotateZ(HALF_PI * -1);
cirno.scale(5);
}
void settings()
{
//size(800, 400, P3D);
fullScreen(P3D);
smooth(8);
System.setProperty("jogl.disable.openglcore", "true");
}
void setup() {
surface.setResizable(true);
//println("loading textures");
//cirnoTexture = loadImage("cirno_low_u1_v1.jpeg");
//rockTexture = loadImage("rockTexture.png");
//println("finished loading textures: " + cirnoTexture);
loadMeshes();
stars = new Star[0];
kryptonit = new Kryptonit[0];
raumschiff = new Raumschiff(width/2, height/4*3, cirno);
leben = 5;
gameOverFont = createFont("Arial", 36, true);
gameOverFontSmall = createFont("Arial", 16, true);
for (int i = 0; i < schwierigkeit; i++) {
stars = (Star[]) append(stars, new Star(int(random(50, width-150)), int(random(50, height-100)), int(random(5, 15)), rock));
}
players = new Player[0];
cp5 = new ControlP5(this);
cp5.addTextfield("Name")
.setPosition(width/2-100, height/3*2-20)
.setSize(200, 40)
.setFont(gameOverFontSmall)
.setFocus(false)
.setColor(color(255))
.setAutoClear(false)
.setText("Name")
.setLabel("")
.hide()
.lock()
;
}
void draw() {
background(0);
lights();
switch (zustand) {
case 0:
break;
case 1:
fill(255);
text("Zeit:\t" + minuten + ":" + zeit, width-100, 50);
text("Punkte:\t" + punkte, width-100 , 100);
text("Leben:\t" + leben, width-100, 150);
text("Highscore:\t" + highscore, width-100, 200);
text("schwierigkeit:\t" + schwierigkeit, width-100, 250);
try {
for (int i = 0; i < players.length; i++) {
text(players[i].getName() + " " + players[i].getScore(), width-100, 300+15*i);
}
for (int i = 0; i < stars.length; i++) {
stars[i].zeichnen();
stars[i].drehen(random(0, 0.05), random(0, 0.05), random(0, 0.05));
}
for (int i = 0; i < kryptonit.length; i++) {
kryptonit[i].zeichnen();
}
if (kryptonitAnimationTimer1.getTimeLeftUntilFinish() != .0f) {
raumschiff.zeichnen(color(350-kryptonitAnimationTimer1.getTimeLeftUntilFinish(), 100, 100));
} else {
raumschiff.zeichnen(color(100, 100, 100));
}
} catch (Exception e) { e.printStackTrace(); }
noFill();
stroke(100);
rect(50, 50, width-200, height-150);
fill(0);
noStroke();
rect(0, 0, width-150, 48);
if (gameOver) {
pushMatrix();
fill(255);
textAlign(CENTER, CENTER);
textFont(gameOverFont, 36);
textSize(34);
text("GAME OVER!", width/2, height/2);
textFont(gameOverFontSmall, 16);
textSize(16);
text("Press ENTER to resume", width/2, height/2+30);
cp5.get(Textfield.class, "Name").unlock();
cp5.get(Textfield.class, "Name").show();
popMatrix();
}
else if (paused) {
pushMatrix();
fill(255);
textAlign(CENTER, CENTER);
textFont(gameOverFont, 36);
textSize(34);
text("PAUSED!", width/2, height/2);
textFont(gameOverFontSmall, 16);
textSize(16);
text("PRESS ANY KEY TO RESUME", width/2, height/2+30);
popMatrix();
}
break;
default :
background(0);
break;
}
for (int i = 0; i < stars.length; i++) {
if (stars[i].isVisible && sqrt((stars[i].posX - raumschiff.posX) * (stars[i].posX - raumschiff.posX) + (stars[i].posY - raumschiff.posY) * (stars[i].posY - raumschiff.posY) ) < 25){
stars[i].isVisible = false;
punkte+=stars[i].speed;
if (changeLevel > 0) {
changeLevel--;
}
}
}
if (punkte > highscore) {
highscore = punkte;
}
if (kryptonit.length < schwierigkeit / 5) {
//kryptonit = (Kryptonit[]) append(kryptonit, new Kryptonit(int(random(50, width-150)), int(random(-300, 0))));
}
if (stars.length < schwierigkeit) {
stars = (Star[]) append(stars, new Star(int(random(50, width-150)), int(random(-300, 0)), int(random(5, 15)), rock));
}
for (int i = 0; i < kryptonit.length; i++) {
if (kryptonit[i].isVisible && sqrt((kryptonit[i].posX - raumschiff.posX) * (kryptonit[i].posX - raumschiff.posX) + (kryptonit[i].posY - raumschiff.posY) * (kryptonit[i].posY - raumschiff.posY) ) < 25){
kryptonit[i].isVisible = false;
leben-=1;
kryptonitAnimationTimer1.start();
}
}
if (leben < 1){
gameOver = true;
}
if (punkte % 500 <= 20 && punkte % 500 >= 0 && changeLevel == 0 && zustand == 1) {
schwierigkeit+=5;
changeLevel = 5;
}
if (punkte % 500 > 20) {
changeLevel = 0;
}
if (!paused) {
try {
if (!gameOver) {
for (int i = 0; i < stars.length; i++) {
stars[i].bewegen(schwierigkeit/stars[i].speed+boost);
if (stars[i].posY > height-100){
stars[i] = null;
stars[i] = new Star(int(random(58, width-202)), int(random(-300, 0)), int(random(5, 15)), rock);
}
}
for (int i = 0; i < kryptonit.length; i++){
kryptonit[i].bewegen(schwierigkeit/10+boost);
if (kryptonit[i].posY > height-100){
kryptonit[i] = null;
kryptonit[i] = new Kryptonit(int(random(58, width-202)), int(random(-300, 0)));
}
}
}
} catch (Exception e) { e.printStackTrace(); }
}
if (keysPressed[56]){
boost = 5;
}
else {
boost = 0;
}
if (keysPressed[52] && !gameOver && !paused){
try {
raumschiff.bewegen(-7);
if (keysPressed[32]) {
raumschiff.bewegen(-10);
}
} catch (Exception e) { e.printStackTrace(); }
}
if (keysPressed[54] && !gameOver && !paused){
try {
raumschiff.bewegen(7);
if (keysPressed[32]) {
raumschiff.bewegen(10);
}
} catch (Exception e) { e.printStackTrace(); }
}
surface.setTitle((int)frameRate+"fps");
}
void keyPressed() {
if (gameOver && key == ENTER) {
players = (Player[]) append(players, new Player(cp5.get(Textfield.class, "Name").getText(), punkte));
cp5.get(Textfield.class, "Name").lock();
cp5.get(Textfield.class, "Name").hide();
for (int i = 0; i < saves.size(); i++) {
JSONObject playerJSONObject = new JSONObject();
playerJSONObject.setInt("id", i);
playerJSONObject.setString(cp5.get("Name", cp5.get(Textfield.class, "Name").getText()).toString(), "");
playerJSONObject.setInt("score", punkte);
}
saveJSONArray(saves, "data/highscores.json");
schwierigkeit = 20;
paused = true;
gameOver = false;
leben = 5;
punkte = 0;
timer1.reset(CountdownTimer.StopBehavior.STOP_IMMEDIATELY);
timer1.start();
zeit = 0;
stars = null;
stars = new Star[0];
for (int i = 0; i < schwierigkeit; i++) {
stars = (Star[]) append(stars, new Star(int(random(50, width-150)), int(random(50, height-100)), int(random(5, 15)), rock));
}
kryptonit = null;
kryptonit = new Kryptonit[0];
}
keysPressed[key] = true;
}
void keyReleased() {
keysPressed[key] = false;
}
void keyTyped() {
if (key == 'p' || key == 'P') {
if (!gameOver) {
paused = !paused;
if (paused) {
timer1.stop(CountdownTimer.StopBehavior.STOP_IMMEDIATELY);
}
else {
timer1.start();
}
}
}
if (paused && !gameOver && key != 'p' && key != 'P') {
paused = false;
timer1.start();
}
}
void onTickEvent(CountdownTimer t, long timeLeftUntilFinish) {
if (t == timer1) {
zeit++;
}
}
void onFinishEvent(CountdownTimer t) {
if (t == timer1) {
timer1.reset(CountdownTimer.StopBehavior.STOP_AFTER_INTERVAL);
timer1.start();
zeit = 0;
minuten++;
}
}
abstract class Flugobjekt {
public int posX;
public int posY;
public int rot;
public int speed;
boolean isVisible = true;
PShape symbol;
abstract void bewegen (int amount);
}
abstract class UFO extends Flugobjekt {
}
public class Star extends UFO {
float rotationX, rotationY, rotationZ;
public Star (int x, int y, int spd, PShape symbol) {
posX = x;
posY = y;
rot = int(random(0, 360));
speed = spd;
// use a reference to the preloaded PShape (instead of loading a the .obj again for each instance)
this.symbol = symbol;
//symbol = loadShape("rockWithMTL.obj");
//symbol.setTexture(rockTexture);
//symbol.scale(0.2);
/*
fill(255);
stroke(255);
strokeWeight(2);
symbol = createShape();
symbol.beginShape();
symbol.vertex(0, -5);
symbol.vertex(1.4, -2);
symbol.vertex(4.7, -1.5);
symbol.vertex(2.3, 0.7);
symbol.vertex(2.9, 4.0);
symbol.vertex(0, 2.5);
symbol.vertex(-2.9, 4);
symbol.vertex(-2.3, 0.7);
symbol.vertex(-4.7, -1.5);
symbol.vertex(-1.4, -2);
symbol.endShape(CLOSE);
/*/
}
public void zeichnen (){
// skip if PShape (or it's texture) isn't loaded yet)
if(symbol == null){
return;
}
if (isVisible) {
pushMatrix();
translate(posX, posY);
//rotate(rot);
rotateX(rotationX);
rotateY(rotationY);
rotateZ(rotationZ);
//scale(0.2);
shape(symbol);
popMatrix();
}
}
public void bewegen (int amount) {
posY = posY + amount;
}
public void drehen (float xAmount, float yAmount, float zAmount) {
rotationX += xAmount;
rotationY += yAmount;
rotationZ += zAmount;
// symbol.rotateX means all vertices inside the shape will be updated
// use rotateX() then shape() to simply render the same underlying PShape vertex data without updating it all the time
//symbol.rotateX(xAmount);
//symbol.rotateY(yAmount);
//symbol.rotateZ(zAmount);
}
}
public class Kryptonit extends UFO {
public Kryptonit (int x, int y) {
posX = x;
posY = y;
rot = int(random(0, 360));
fill(0);
stroke(255, 0, 0);
strokeWeight(2);
symbol = createShape();
symbol.beginShape();
symbol.vertex(0, -5);
symbol.vertex(1.4, -2);
symbol.vertex(4.7, -1.5);
symbol.vertex(2.3, 0.7);
symbol.vertex(2.9, 4.0);
symbol.vertex(0, 2.5);
symbol.vertex(-2.9, 4);
symbol.vertex(-2.3, 0.7);
symbol.vertex(-4.7, -1.5);
symbol.vertex(-1.4, -2);
symbol.endShape(CLOSE);
}
public void zeichnen (){
if (isVisible) {
pushMatrix();
translate(posX, posY);
rotate(rot);
shape(symbol);
popMatrix();
}
}
public void bewegen (int amount) {
posY = posY + amount;
}
}
public class Raumschiff extends Flugobjekt {
public Raumschiff (int x, int y, PShape symbol) {
posX = x;
posY = y;
fill(100);
noStroke();
this.symbol = symbol;
//symbol = loadShape("cirno_lowWithMTL.obj");//createShape(ELLIPSE, 0, 0, 50, 50);
//symbol.setTexture(cirnoTexture);
//symbol.rotateY(HALF_PI);
//symbol.rotateZ(HALF_PI * -1);
////symbol.rotateX(0.5);
//symbol.scale(5);
/* (Raumschiff)
symbol = createShape();
symbol.beginShape();
symbol.vertex(25, 0);
symbol.vertex(30, 5);
symbol.vertex(30, 5);
symbol.vertex(32, 12);
symbol.vertex(28, 20);
symbol.vertex(31, 28);
symbol.vertex(27, 25);
symbol.vertex(25, 29);
symbol.vertex(23, 25);
symbol.vertex(19, 28);
symbol.vertex(22, 20);
symbol.vertex(18, 12);
symbol.vertex(20, 5);
symbol.endShape(CLOSE);
//*/
}
public void zeichnen (color farbe){
if (isVisible) {
pushMatrix();
symbol.setFill(farbe);
translate(posX, posY);
rotate(rot);
shape(symbol);
popMatrix();
}
}
public void bewegen (int amount) {
posX+=amount;
if (posX < 50) posX = 50;
if (posX > width-150) posX = width-150;
}
}
public class Player {
private String name;
private int score;
public Player (String n, int s) {
name = n;
score = s;
}
public String getName() {
return name;
}
public int getScore() {
return score;
}
}
Here's an example of pre-allocating a number of objects to be reused (a-la object pooling), instead of constant reinstantiation (which has it's costs):
PShape rock;
int numRocks = 25;
Rock[] rocks = new Rock[numRocks];
float halfWidth;
float halfHeight;
void setup(){
size(900, 900, P3D);
rock = loadShape("rockWithMTL.obj");
// ideally the mesh would already been scaled down to avoid this
rock.scale(0.2);
halfWidth = width * 0.5;
halfHeight = height * 0.5;
for(int i = 0 ; i < numRocks; i++){
rocks[i] = new Rock(rock, random(-halfWidth, halfWidth), random(-halfHeight, halfHeight));
}
}
void draw(){
background(0);
lights();
translate(width * 0.5, height * 0.5, 0);
for(int i = 0 ; i < numRocks; i++){
rocks[i].draw();
}
surface.setTitle((int)frameRate + "fps");
}
class Rock{
PShape mesh;
PVector position = new PVector();
PVector velocity = new PVector();
PVector rotationAxis = new PVector();
float rotationAngle = 0;
Rock(PShape mesh, float x, float y){
this.mesh = mesh;
position.x = x;
position.y = y;
velocity.y = random(1, 10);
// pick a random rotation axis
rotationAxis.set(random(1), random(1), random(1));
}
void draw(){
// update
// increment position
position.add(velocity);
// increment rotation
rotationAngle += 0.1;
// object pool behaviour: reset if off screen (no need to re-allocate a new instance)
if(position.y > halfHeight + 100){
position.x = random(-halfWidth, halfWidth);
position.y = -halfHeight - 100;
}
// draw
pushMatrix();
translate(position.x, position.y, position.z);
rotate(rotationAngle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
shape(mesh);
popMatrix();
}
}
Also, here's a super basic demo on encapsulating states. It's a bit hacky because each state know of the other, but shows each could behave as it's own "sketch" that can live in it's own tab and only override it's specific behaviour:
StartScreen start;
GameScreen game;
HighScoreScreen highScore;
StateScreen currentScreen;
void setup(){
size(300, 300);
textAlign(CENTER, CENTER);
textSize(18);
start = new StartScreen();
game = new GameScreen();
highScore = new HighScoreScreen();
currentScreen = start;
}
void draw(){
background(0);
currentScreen.draw();
}
void keyPressed(){
currentScreen.keyPressed();
}
class StateScreen {
StateScreen(){
setup();
}
void setup(){ println(this,"setup()"); }
void draw(){}
void keyPressed(){}
}
class StartScreen extends StateScreen{
void draw(){
fill(sin(frameCount * 0.1) * 127);
text("push any key to\nstart", width * 0.5, height * 0.5);
}
void keyPressed(){
currentScreen = game;
}
}
class GameScreen extends StateScreen{
void draw(){
fill(0, sin(frameCount * 0.1) * 127, 0);
text("push SPACE key to go to\nhigh score screen", width * 0.5, height * 0.5);
}
void keyPressed(){
currentScreen = highScore;
}
}
class HighScoreScreen extends StateScreen{
void draw(){
fill(random(255), random(255), random(255));
text("push SPACE key to go to\nstart screen", width * 0.5, height * 0.5);
}
void keyPressed(){
currentScreen = start;
}
}

Stop and Restart game in Processing on button press

I'm currently working on a small game to learn Processing.
I want my game to stop when I press "Stop" and want my game to reset/restart when "Stop" changed to Start.
(When I click on the button, it changes Stop to Start and back to Stop etc etc. (so basically, I have 1 'button')
I'm struggling and can't find a solution on internet / stackoverflow so maybe someone can help me?
(# mousePressed's if else I need the 'stop and restart function')
float x = width/2;
float speed = 2;
boolean textHasBeenClicked = false;
int aantalRaak = 0;
int aantalMis = 0;
int positieText = 20;
void setup() {
background(0);
size(600,500);
}
void draw() {
clear();
move();
display();
smooth();
//Scoreboard bovenaan
fill(255);
textSize(20);
textAlign(LEFT);
text("Aantal geraakt: " + aantalRaak,0, positieText); text("Aantal gemist: " + aantalMis, width/2, positieText);
//button onderaan
fill(0,255,0);
rect(width/2-40, height-40, 100, 50);// draw anyway...
}
void mousePressed() {
// toggle
textHasBeenClicked = ! textHasBeenClicked;
fill(0);
if (textHasBeenClicked) {
// display text 2
textSize(30);
textAlign(CENTER);
text("Stop" , width/2,height-10);
}
else {
// display text 1
textSize(30);
textAlign(CENTER);
text("Start" , width/2,height-10);
}
}
void move() {
x = x + speed;
if (x > width) {
x = 0;
}
}
void display(){
//schietschijf
float y = height/2;
noStroke();
fill(255, 0, 0);
ellipse(x, y, 40, 40);
fill(255);
ellipse(x, y, 30, 30);
fill(255, 0, 0);
ellipse(x, y, 20, 20);
fill(255);
ellipse(x, y, 10, 10);
}
You should try to break your problem down into smaller steps and take those steps on one at a time. You're really asking two questions:
How do I show a stop button that turns into a start button?
How do I reset a sketch?
For the first question, you can create a boolean variable. Use that variable in your draw() function, and then modify that variable in the mousePressed() function.
boolean running = false;
void draw() {
fill(0);
if (running) {
background(255, 0, 0);
text("Stop", 25, 25);
} else {
background(0, 255, 0);
text("Start", 25, 25);
}
}
void mousePressed() {
running = !running;
}
Then for resetting the sketch, you can create a function that changes all of your variables back to their default values. Here's a simple example:
float circleY;
void setup() {
size(100, 500);
}
void draw() {
background(0);
circleY++;
ellipse(width/2, circleY, 20, 20);
}
void reset() {
circleY = 0;
}
void mousePressed() {
reset();
}
Try to work from small examples like this instead of your full program, and post a MCVE if you get stuck. Good luck.
You may consider implementing a while loop. I don't know what library you're using for input, so I can't tell you exactly what to do. But something along the lines of:
while(!InputReceived) {
if(CheckForMouseInput()) // Assuming CheckForMouseInput returns true if input was detected
break // Input was detected, now do stuff based on that.
else {
// Must #include <thread> and #include <chrono>
// Wait a bit...
continue; // Jump back to the top of the loop, effectively restarting it.
}
Would probably suit your needs. That is what I would do, at least. After the loop breaks, the game is effectively restarted, and you can do whatever you need to do based on that.

Moving around inside a larger-than-screen image

Context: Processing v3; Windows 10
I have an 853x2048 pixel image that I want to display fullsize on the screen. A large part of it will disappear off the bottom. I want to use Processing to navigate around inside the image, motion-path like, pause at certain points and also zoom in and out.
The code below is adapted from a demo of the Robot class as I thought that would be the go for moving around inside the image.
This code works but I can't as yet figure out how to move the viewport. And then there's the issue of zooming (which I am yet to address.)
How does one move the mouse so that the image moves or how does one move the image with respect to the viewport?
//
// how to use java.awt.Robot class in processing ...
//
import java.awt.*;
import java.awt.event.*;
Robot robot;
PFont pfont;
Point save_p;
PImage img;
void setup() {
try {
robot = new Robot();
robot.setAutoDelay(0);
}
catch (Exception e) {
e.printStackTrace();
}
surface.setResizable(true);
fullScreen();
img = loadImage("bigpic.jpg");
pfont = createFont("Impact", 32);
}
void draw() {
background(#ffffff);
fill(#000000);
imageMode(CORNERS);
image(img, 0, 0, 640*3, 480*8);
Point p = getGlobalMouseLocation();
textFont(pfont);
text("now x=" + (int)p.getX() + ", y=" + (int)p.getY(), 10, 32);
if (save_p != null) {
text("save x=" + (int)save_p.getX() + ", y=" + (int)save_p.getY(), 10, 64);
}
}
void keyPressed() {
switch(key) {
case 's':
save_p = getGlobalMouseLocation();
break;
case 'm':
if (save_p != null) {
mouseMove((int)save_p.getX(), (int)save_p.getY());
}
break;
case 'c':
break;
case 't':
translate(2000, 0);
break;
case ' ':
if (save_p != null) {
mouseMoveAndClick((int)save_p.getX(), (int)save_p.getY());
}
break;
}
}
Point getGlobalMouseLocation() {
// java.awt.MouseInfo
PointerInfo pointerInfo = MouseInfo.getPointerInfo();
Point p = pointerInfo.getLocation();
return p;
}
void mouseMove(int x, int y) {
robot.mouseMove(x, y);
}
void mouseMoveAndClick(int x, int y) {
robot.mouseMove(x, y);
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
robot.waitForIdle();
}
I'm a little confused by what you're doing with the Robot class.
But in any case, basically what you want to do is draw the image so its Y value is lower, which will cause the image to rise so you can see more of it. Here's a small example:
PImage img;
float imageY = 0;
void setup() {
img = loadImage("bigpic.jpg");
}
void draw() {
image(img, 0, imageY);
imageY--;
}
You can do something similar with resizing, just by passing in more parameters to the image() function.
But it sounds like what you're really looking for are the transformation functions. See the transform section of the Processing reference for more info, but basically you would call the translate() function to move the origin. You could also call the scale() function to "zoom" in and out.
Here's a little example that moves around a rectangle instead of an image:
void setup(){
size(500, 500);
}
void draw(){
background(64);
translate(-mouseX, -mouseY);
rect(0, 0, width, height);
}
Don't forget that Processing comes with a ton of examples. In the Processing editor, go to File > Examples.

Moving image with the cursor

So, I'm working on a processing project that lets me use an image as a cursor but I've been having problems with the cursor image because it has been constantly blinking. I read that if the cursor image is too big it has a tendency to constantly blink. However, I was wondering if there was any way that I could keep the size of my image, while still maintaining it as a cursor. OR,
I was wondering if there was a code that lets me press the image and drag it around the screen. :/
Here's the code that I've been using.
// Declaring a variable of type PImage
PImage img;
PImage img2;
void setup() {
size(815,514);
// Make a new instance of a PImage by loading an image file
img = loadImage("preamble.jpg");
img2 = loadImage("blackgun.png");
}
void draw() {
background(0);
// Draw the image to the screen at coordinate (0,0)
image(img,0,0);
//using the image as the cursor
if (mouseX < 50) {
cursor(img2);
} else {
cursor(img2);
}
}
In the reference they say: "it is recommended to make the size 16x16 or 32x32 pixels" about the image to be used as cursor. You can do this by calling resize:
img2 = loadImage("blackgun.png");
img2.resize(32,32);
Also there is no point in the lines:
if (mouseX < 50) {
cursor(img2);
} else {
cursor(img2);
}
As either way you end up with the same img2 as cursor image.
YOu can just use:
image(img, mouseX, mouseY);
but the cursor will be over the image.
That's a simple and poor drag...
I have here an old example of a little better drag and drop, it is using rects() intead of images but the idea is the same and you can easily adapt it to use images:
DragMe[] drags = new DragMe[40];
void setup() {
size(400, 400);
for (int i = 0; i < drags.length; i++) {
drags[i] = new DragMe();
}
}
void draw() {
background(255);
for (int i = 0; i < drags.length; i++) {
drags[i].display();
drags[i].update();
}
}
void mousePressed() {
for (int i = 0; i < drags.length; i++) {
if (!drags[i].isOver())
drags[i].dontMove = true;
drags[i].offset_x = mouseX - drags[i].pos_x;
drags[i].offset_y = mouseY - drags[i].pos_y;
}
}
void mouseReleased() {
for (int i = 0; i < drags.length; i++) {
drags[i].locked = false;
drags[i].dontMove = false;
}
}
class DragMe {
float pos_x, pos_y, SIZE = 20;
float prev_x, prev_y;
boolean locked;
boolean dontMove;
color c = color (0, 170, 170);
float offset_x, offset_y;
DragMe() {
pos_x = random(width-SIZE);
pos_y = random(height-SIZE);
}
void update() {
if (isOver() && !locked && !dontMove || locked && !dontMove )
c = color (170);
else
c = color (0, 170, 170);
if (isClicked()) {
locked = true;
}
if (locked && !dontMove) {
pos_x = mouseX - offset_x;
pos_y = mouseY - offset_y;
}
}
void display() {
fill(c);
rect(pos_x, pos_y, SIZE, SIZE);
}
boolean isOver() {
float right_x = pos_x + SIZE;
float bottom_y = pos_y + SIZE;
return mouseX >= pos_x && mouseX <= right_x &&
mouseY >= pos_y && mouseY <= bottom_y;
}
boolean isClicked() {
return isOver() && mousePressed && !dontMove;
}
}

XNA SpriteSheet not working

I have added logic to show the Sprite animating but it's simply not showing on the screen. What am I doing wrong?
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input.Touch;
namespace MyXNAGame.Game_Classes
{
public class Player : Sprite
{
private int _frameCount = 6;
private int _frameIndex;
private Rectangle[] _frames;
public float _jumpVelocity = 12f;
public PlayerState _playerState;
public Rectangle BoundingBox
{
get { return new Rectangle {X = (int) Position.X, Y = (int) Position.Y, Height = Texture.Height, Width = Texture.Width}; }
}
public void Initialize()
{
_frames = new Rectangle[_frameCount];
int width = Texture.Width / _frameCount;
for (int i = 0; i < _frameCount; i++)
{
_frames[i] = new Rectangle(i*width, 0, width, Texture.Height);
}
}
public void Load(ContentManager contentManager, string assetName)
{
Texture = contentManager.Load<Texture2D>(assetName);
}
public void Update(GameTime gameTime)
{
while (TouchPanel.IsGestureAvailable)
{
GestureSample gestureSample = TouchPanel.ReadGesture();
if (gestureSample.GestureType == GestureType.Tap)
{
if (_playerState == PlayerState.Running)
{
_playerState = PlayerState.NormalJump;
}
}
if (gestureSample.GestureType == GestureType.Hold)
{
if (_playerState == PlayerState.Running)
{
_playerState = PlayerState.LongJump;
}
}
}
// NormalJump Logic
switch (_playerState)
{
case PlayerState.NormalJump:
Position.Y -= _jumpVelocity;
_jumpVelocity -= 0.5f;
if (_jumpVelocity == 0)
{
_playerState = PlayerState.Falling;
}
break;
case PlayerState.LongJump:
Position.Y -= _jumpVelocity;
_jumpVelocity -= 0.5f;
if (_jumpVelocity == 0)
{
_playerState = PlayerState.Falling;
}
break;
case PlayerState.Falling:
Position.Y += _jumpVelocity;
_jumpVelocity += 0.5f;
break;
case PlayerState.Running:
_frameIndex++;
if (_frameIndex > 5)
{
_frameIndex = 0;
}
break;
}
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(Texture, Position, _frames[_frameIndex], Color.White, 0, new Vector2(0, 0), new Vector2(0, 0), SpriteEffects.None, 0);
}
}
}`
Can anyone see the obvious mistake? I am using WP7
I changed the 'Scale' parameter in the Draw() method from new Vector(0,0) to new Vector(1,1) as obviously, having a Scale of 0 will not show anything at all.

Resources