I am trying to build a little 'simulation' game.
This game has no real purpose, I am just making small little projects while I try and learn the in's and out's of some beginner programming.
This is my goal:
On the processing canvas, there are multiple 'Nodes' that represent where a player can move to.
The user will input where the player is and where they want to move to. (referencing the nodes)
The program will determine the most efficient route using the A* algorithm.
Once the route has been determined, the player (represented by a Circle()) will move from node to node in a straight line.
Once the player has stopped, the program will know which node the player is currently at AND will wait for further instructions.
I have somehow managed to scrape together the first three of my goals but the second half has been causing me major confusion and headaches.
What I have tried (Goal 4).
I am using a custom library for the A* algorithm which can be found here: http://www.lagers.org.uk/pfind/ref/classes.html.
When the algorithm drew the lines for the optimal route, I would store the X,Y position of each node into an ArrayList.
I would then feed that ArrayList data into my Player Class that would move the circle on the screen via the X/Y positions from the ArrayList.
The issue I had, is that once the player moved to the first node, I had no way of reporting that the player had stopped moving and is ready to move onto the next ArrayList X/Y position.
I managed a workaround by incrementing the ArrayList every 5 seconds using Millis() but I know this is a terrible way of achieving my goal.
This probably does not make a lot of sense but here is a picture of my current output.
I have told the program that I want the Blue Circle to travel from Node 0 to Node 8 on the most efficient route. My current code would move copy the X/Y positions of Node 0,2,8 and save them into an ArrayList.
That ArrayList information would be fed into the player.setTarget() method every 5 seconds to allow time for the circle to move.
Ideally, I would like to scrap the time delay and have the class report when the player has moved to the node successfully AND which node the player is currently on.
import pathfinder.*;
import java.lang.Math;
// PathFinding_01
Graph graph;
// These next 2 are only needed to display
// the nodes and edges.
GraphEdge[] edges;
GraphNode[] nodes;
GraphNode[] route;
// Pathfinder algorithm
IGraphSearch pathFinder;
// Used to indicate the start and end nodes as selected by the user.
GraphNode startNode, endNode;
PImage bg;
Player midBlue;
Player midRed;
int lastHittingBlue = 99;
int lastHittingRed = 99;
int blueSide = 0;
int redSide = 1;
boolean nodeCount = true;
boolean firstRun = true; //Allows data to be collected on the first node.
boolean movement;
int count;
int x;
int y;
float start;
float runtime;
int test = 1;
// create ArrayList for route nodes
ArrayList<Float> xPos;
ArrayList<Float> yPos;
void setup() {
size(1200,1000); //Set size of window to match size of the background image.
bg = loadImage("background.png");
bg.resize(1200,1000);
start = millis();
textSize(20);
// Create graph
createGraph();
// Get nodes and edges
nodes = graph.getNodeArray();
edges = graph.getAllEdgeArray();
// Now get a path finder object
pathFinder = new GraphSearch_Astar(graph);
// Now get a route between 2 nodes
// You can change the parameter values but they must be valid IDs
pathFinder.search(0,8);
route = pathFinder.getRoute();
//Initialise the X/Y position arraylist.
xPos = new ArrayList<Float>();
yPos = new ArrayList<Float>();
drawGraph();
drawPath();
midBlue = new Player(lastHittingBlue, blueSide);
midRed = new Player(lastHittingRed, redSide);
}
void draw() {
background(0);
text((float)millis()/1000, 10,height/6);
text(start/1000, 10,height/3);
runtime = millis() - start;
text(runtime/1000, 10,height/2);
if (runtime >= 5000.0) {
start = millis();
float printX = midBlue.getXPos();
float printY = midBlue.getYPos();
int pX = round(printX);
int pY = round(printY);
print(pX, " ", pY, "\n");
test += 1;
}
drawGraph();
drawPath();
movement = midBlue.movementCheck();
midBlue.setTargetPosition(xPos.get(test), yPos.get(test));
midBlue.drawPlayer();
text( "x: " + mouseX + " y: " + mouseY, mouseX + 2, mouseY );
//noLoop();
}
void drawGraph() {
// Edges first
strokeWeight(2);
stroke(180, 180, 200);
for (int i = 0; i < edges.length; i++) {
GraphNode from = edges[i].from();
GraphNode to = edges[i].to();
line(from.xf(), from.yf(), to.xf(), to.yf());
}
// Nodes next
noStroke();
fill(255, 180, 180);
for (int i = 0; i < nodes.length; i++) {
GraphNode node = nodes[i];
ellipse(node.xf(), node.yf(), 20, 20);
text(node.id(), node.xf() - 24, node.yf() - 10);
}
}
void drawPath() {
strokeWeight(10);
stroke(200, 255, 200, 160);
for (int i = 1; i < route.length; i++) {
GraphNode from = route[i-1];
GraphNode to = route[i];
while (firstRun) {
xPos.add(from.xf());
yPos.add(from.yf());
firstRun = false;
}
xPos.add(to.xf());
yPos.add(to.yf());
line(from.xf(), from.yf(), to.xf(), to.yf());
if (nodeCount == true) {
count = route.length;
nodeCount = false;
}
}
}
public void createGraph() {
graph = new Graph();
// Create and add node
GraphNode node;
// ID X Y
node = new GraphNode(0, 175, 900);
graph.addNode(node);
node = new GraphNode(1, 190, 830);
graph.addNode(node);
node = new GraphNode(2, 240, 890);
graph.addNode(node);
node = new GraphNode(3, 253, 825);
graph.addNode(node);
node = new GraphNode(4, 204, 750);
graph.addNode(node);
node = new GraphNode(5, 315, 770);
graph.addNode(node);
node = new GraphNode(6, 325, 880);
graph.addNode(node);
node = new GraphNode(7, 440, 880);
graph.addNode(node);
node = new GraphNode(8, 442, 770);
graph.addNode(node);
node = new GraphNode(9, 400, 690);
graph.addNode(node);
node = new GraphNode(10, 308, 656);
graph.addNode(node);
node = new GraphNode(11, 210, 636);
graph.addNode(node);
// Edges for node 0
graph.addEdge(0, 1, 0, 0);
graph.addEdge(0, 2, 0, 0);
graph.addEdge(0, 3, 0, 0);
// Edges for node 1
graph.addEdge(1, 4, 0, 0);
graph.addEdge(1, 5, 0, 0);
graph.addEdge(1, 10, 0, 0);
// Edges for node 2
graph.addEdge(2, 5, 0, 0);
graph.addEdge(2, 6, 0, 0);
graph.addEdge(2, 8, 0, 0);
// Edges for node 3
graph.addEdge(3, 5, 0, 0);
graph.addEdge(3, 8, 0, 0);
graph.addEdge(3, 10, 0, 0);
// Edges for node 4
graph.addEdge(4, 10, 0, 0);
graph.addEdge(4, 11, 0, 0);
// Edges for node 5
graph.addEdge(5, 8, 0, 0);
graph.addEdge(5, 9, 0, 0);
graph.addEdge(5, 10, 0, 0);
// Edges for node 6
graph.addEdge(6, 7, 0, 0);
graph.addEdge(6, 8, 0, 0);
// Edges for node 7
graph.addEdge(7, 0, 0, 0);
// Edges for node 7
graph.addEdge(9, 0, 0, 0);
// Edges for node 7
//graph.addEdge(10, 0, 0, 0);
// Edges for node 7
graph.addEdge(11, 0, 0, 0);
}
class Player {
int lastHitting;
int side; //0 = Blue, 1 = Red.
float xPos;
float yPos;
float xTar;
float yTar;
color circleColour = color(255,0,0);
boolean isPlayerStopped;
int xDir;
int yDir;
Player(int lastHitting, int side) {
this.lastHitting = lastHitting;
this.side = side;
/* Set the Colour of the circle depending on their side selection */
if (this.side == 0) {
circleColour = color(0,0,255);
xPos = 180;
yPos = 900;
} else if (this.side == 1) {
circleColour = color(255,0,0);
xPos = 990;
yPos = 125;
}
}
void drawPlayer() {
fill(circleColour);
circle(xPos,yPos,35);
float speed = 100.0;
PVector dir = new PVector(xTar - xPos, yTar - yPos);
while (dir.mag() > 1.0) {
dir.normalize();
dir.mult(min(speed, dir.mag()));
xPos += dir.x;
yPos += dir.y;
isPlayerStopped = false;
}
if (dir.mag() < 1.0) {
isPlayerStopped = true;
}
}
void setTargetPosition(float targetX, float targetY) {
xTar = targetX;
yTar = targetY;
}
boolean movementCheck() {
return isPlayerStopped;
}
float getXPos() {
return xPos;
}
float getYPos() {
return yPos;
}
}
Thank you for your help in advance. I know this is a bit of a loaded question. I am really just looking for direction, I have tried a lot of different things and I'm not sure what tool I am supposed to use to help me progress.
Please don't flame my terrible code too much, I am still very new to all of this.
I am not going to flame your terrible code because I know how steep the learning curve is for what you're doing and I respect that. This said, I think that you would gain much more insight on what's going on if you ditched the library and coded your A* yourself.
If it comes to that, I can help later, but for now, here's what we'll do: I'll point out how you can get this result:
And as a bonus, I'll give you a couple tips to improve on your coding habits.
I can see that you kinda know what you're doing by reading your own understanding of the code (nice post overall btw), but also in the code I can see that you still have a lot to understand about what you're really doing.
You should keep this exactly as it is right now. Email it to yourself to you receive in in one year, this way next year while you despair about getting better you'll have the pleasant surprise to see exactly how much you improved - or, in my case, I just decided that I had been retarded back then and still was, but I hope that you're not that hard on yourself.
As there is A LOT of space for improvement, I'm just going to ignore all of it except a couple key points which are not project specific:
Use explicit variable names. Some of your variables look like they are well named, like nodeCount. Except that this variable isn't an integer; it's a boolean. Then, it should be named something like nodesHaveBeenCounted. Name your variables like if an angry biker had to review your code and he'll break one of your finger every time he has to read into the code to understand what's a variable purpose. While you're at it, try not to shorten a variable name even when it's painfully obvious. xPos should be xPosition. This apply to method signatures, too, both with the method's name (which you're really good at, congrats) and the method's parameters.
Careful with the global variables. I'm not against the idea of using globals, but you should be careful not to just use them to bypass scope. Also, take care not to name local variables and global variables the same, like you did with xPos, which may be an ArrayList or a float depending where you are in the code. Be methodic: you can add something to all your global variables which clearly identify them as globals. Some people name prefix them with a g_, like g_xPos. I like to just use an underscore, like _xPos.
When a method does more than one thing, think about splitting it in smaller parts. It's waaay easier to debug the 8 lines where a value is updated than to sift through 60 lines of code wondering where the magic is happening.
Now here are the changes I made to make the movements smoother and avoid using a timer.
In the global variables:
Rename xPos into xPosArray or something similar, as long as it's not overshadowed by the Player's xPos modal variable. Do the same with the yPos ArrayList.
In the setup() method:
Add this line as the last line of the method (as long as it's after instantiating midBlue and running the drawPath method it'll be right):
midBlue.setTargetPosition(xPosArray.get(test), yPosArray.get(test));
In the draw() method:
Remove the if (runtime >= 5000.0) {...} block entirely, there's no use for it anymore.
Remove these lines:
movement = midBlue.movementCheck();
midBlue.setTargetPosition(xPosArray.get(test), yPosArray.get(test));
In the Player.drawPlayer() method:
Erase everything after and including the while. Replace it with these lines (they are almost the same but the logic is slightly different):
if (dir.mag() > 1.0) {
dir.normalize();
dir.mult(min(speed, dir.mag()));
xPos += dir.x;
yPos += dir.y;
} else {
// We switch target only once, and only when the target has been reached
setTargetPosition(xPosArray.get(test), yPosArray.get(test));
}
Run the program. You don't need a timer anymore. What's the idea? It's simple: we only change the Player's target once the current target has been reached. Never elsewhere.
Bonus idea: instead of a global variable for your array of coordinates, each player should have it's own ArrayList of coordinates. Then each Player will be able to travel independently.
Have fun!
Related
How do you get a certain object to hit another object?
I am making a game where trash is falling from the sky, you are meant to drag and drop the trash into the correct bins. How do I make it so when you drag it into the regional area of the bin is, it gives you a score and resets again. I have made the score & lives function but how do you get it to hit the bin and give you a score?
Here's the progress I have made so far:
PImage scene, can, brownB, blueB, greenB;
int can_x = -100; //CAN IS HIDDEN FOR NOW
int can_y = 500;
int canX = 100;
int canY = 50;
int canwidth = width/2;
int canheight = height/2;
int score = 0;
int lives = 3;
int can_count = 0;
int quit = 0;
boolean mouseInCan = false;
void setup() // Entry point (start of program), runs once
{
size(800,600,P2D); // Create a Window, must be same size as scene
scene = loadImage("backround.png"); // load image and data into scene data structure
can = loadImage("can.png"); // load image of can into the GPU
greenB = loadImage("green_bin.png");
textureMode(NORMAL); // Scale texture Top right (0,0) to (1,1)
blendMode(BLEND); // States how to mix a new image with the one behind it
noStroke(); // Do not draw a line around objects
canX = 0 + (int)random(800); // Choose drop starting position
canY = 0;
}
//check if the mouse is in the can
void mousePressed() {
if (mouseX > canX && mouseX < canX + canwidth && mouseY > canY && mouseY < canY + canheight) {
mouseInCan = true;
}
}
//if the mouse is in the can, then move it when the mouse is dragged
void mouseDragged() {
if (mouseInCan) {
float deltaX = mouseX - pmouseX;
float deltaY = mouseY - pmouseY;
canX += deltaX;
canY += deltaY;
}
}
//when we let go of the mouse, stop dragging the can
void mouseReleased() {
mouseInCan = false;
}
void draw()
{
background(scene); // Display background image referenced by scene
image(can, canX, canY, canwidth, canheight); //Can is drawn at position
image(greenB, 100, 450, width/8, height/4);
pushMatrix(); // Store current location of origin (0,0)
translate(can_x,can_y); // Change origin (0,0) for drawing to (drop_x,drop_y)
beginShape(); // Open graphics pipeline
texture(can); // Tell GPU to use drop to texture the polygon
vertex( -20, -20, 0, 0); // Load vertex data (x,y) and (U,V) texture data into GPU
vertex(20, -20, 1, 0); // Square centred on (0,0) of width 40 and height 40
vertex(20, 20, 1, 1); // Textured with an image of a drop
vertex( -20, 20, 0, 1);
endShape(CLOSE); // Tell GPU you have loaded shape into memory.
popMatrix();
canY += 1.5; // Make "drop" move down the screen (two pixels at a time)
if (canY>600) // If y value is entering the bottom of screen
{
canX = 0+(int)random(800); // Restart the drop again in the cloud.
canY = 0;
lives--;
}
if ((canY>100)&&(canY<450)) // If drop is on same level as player
{
if(abs((canX+100)-(canX+450))<25) // And drop is near player
{
score++; // Increase drop count by one (caught)
canX=0+(int)random(800); // Restart a new drop in the cloud
canY=0;
}
}
textSize(18); // Display score information on the screen
fill(0,0,0);
text("Lives: "+lives, 540, 20);
textSize(18);
fill(0,0,0);
text("Score: "+score, 640, 20);
if(quit==1) // Wait five seconds before exiting
{
delay(5000);
exit();
}
if (lives<1) // All lives lost so game over but
{ // return to draw one more time to
quit = 1; // allow "Game Over to be displayed.
}
}
I have tried doing it where the comment with 'If drop is on same level as player' but no luck, The code is a mess right now but I will clean it up, still new to processing.
What you are looking for is some kind of collision detection. There are very complex algorithms out there that can do an accurate detection with complex shapes, at the cost of CPU time, but in your case, just checking the intersection of two rectangles will probably be enough, at least for a start.
First you need to have your bin and can's positions and dimensions stored somewhere. We'll use the enclosing rectangle of each as a collision box.
PImage scene, can, brownB, blueB, greenB;
int canX = 100, canY = 50, canwidth = 80, canheight = 80;
int binX = 100, binY = 450, binW = 100, binH = 150;
You can then adjust the dimensions according to your screen size, but it has to be done after you have initialized it in setup:
void setup() // Entry point (start of program), runs once
{
size(800, 600, P2D); // Create a Window, must be same size as scene
scene = loadImage("backround.png"); // load image and data into scene data structure
can = loadImage("can.png"); // load image of can into the GPU
greenB = loadImage("green_bin.png");
//...
canwidth = width / 10;
canheight = width / 10;
binW = width / 8;
binH = height / 4;
binY = height - binH; //Set the bin at the bottom of the screen
}
Now your elements can be drawn according to the stored values, how and where you intend them, and you can do your collision detection inside your draw loop:
void draw()
{
background(scene); // Display background image referenced by scene
image(can, canX, canY, canwidth, canheight); //Can is drawn at position
image(greenB, binX, binY, binW, binH);
//...
/* If the top of the can is below the top of the bin and the can is
* horizontally within the bin, then we count score
*/
if (canY > binY && canX > binX && canX + canwidth < binX + binW) {
score++; // Increase drop count by one (caught)
canX=0+(int)random(800); // Restart a new drop in the cloud
canY=0;
}
//...
}
I've neglected the check for bottom coordinates of the can relative to the bin, as the bin is at the very bottom, and hitting the bottom kills you.
As was said already, you need some kind of collision detection. I would abstain though from trying to implement any algorithms myself and would instead use a professional library for that like:
https://github.com/locationtech/jts
That way you can also test against the real geometry and the overhead is minimal because these tests are first run againt the bounding boxes and only when that is positive they are refined to the real geometry. In addition you could also use a spatial index to speed up the search.
This project is written entirely from scratch in Java. I've just been bored ever since Covid started, so I wanted something that would take up my time, and teach me something cool. I've been stuck on this problem for about a week now though. When I try to use my near plane clipping method it skews the new vertices to the opposite side of the screen, but sometimes times it works just fine.
Failure Screenshot
Success Screenshot
So my thought is maybe that since it works sometimes, I'm just not doing the clipping at the correct time in the pipeline?
I start by face culling and lighting,
Then I apply a Camera View Transformation to the Vertices,
Then I clip on the near plane
Finally I apply the projection matrix and Clip any remaining off screen Triangles
Code:
This calculates the intersection points. Sorry if it's messy or to long I'm not very experienced in coding, my major is physics, not CS.
public Vertex vectorIntersectPlane(Vector3d planePos, Vector3d planeNorm, Vector3d lineStart, Vector3d lineEnd){
float planeDot = planeNorm.dotProduct(planePos);
float startDot = lineStart.dotProduct(planeNorm);
float endDot = lineEnd.dotProduct(planeNorm);
float midPoint = (planeDot - startDot) / (endDot - startDot);
Vector3d lineStartEnd = lineEnd.sub(lineStart);
Vector3d lineToIntersect = lineStartEnd.scale(midPoint);
return new Vertex(lineStart.add(lineToIntersect));
}
public float distanceFromPlane(Vector3d planePos, Vector3d planeNorm, Vector3d vert){
float x = planeNorm.getX() * vert.getX();
float y = planeNorm.getY() * vert.getY();
float z = planeNorm.getZ() * vert.getZ();
return (x + y + z - (planeNorm.dotProduct(planePos)));
}
//When a triangle gets clipped it has 4 possible outcomes
// 1 it doesn't actually need clipping and gets returned
// 2 it gets clipped into 1 new triangle, for testing these are red
// 3 it gets clipped into 2 new triangles, for testing 1 is green, and 1 is blue
// 4 it is outside the view planes and shouldn't be rendered
public void clipTriangles(){
Vector3d planePos = new Vector3d(0, 0, ProjectionMatrix.fNear, 1f);
Vector3d planeNorm = Z_AXIS.clone();
final int length = triangles.size();
for(int i = 0; i < length; i++) {
Triangle t = triangles.get(i);
if(!t.isDraw())
continue;
Vector3d[] insidePoint = new Vector3d[3];
int insidePointCount = 0;
Vector3d[] outsidePoint = new Vector3d[3];
int outsidePointCount = 0;
float d0 = distanceFromPlane(planePos, planeNorm, t.getVerticesVectors()[0]);
float d1 = distanceFromPlane(planePos, planeNorm, t.getVerticesVectors()[1]);
float d2 = distanceFromPlane(planePos, planeNorm, t.getVerticesVectors()[2]);
//Storing distances from plane and counting inside outside points
{
if (d0 >= 0){
insidePoint[insidePointCount] = t.getVerticesVectors()[0];
insidePointCount++;
}else{
outsidePoint[outsidePointCount] = t.getVerticesVectors()[0];
outsidePointCount++;
}
if (d1 >= 0){
insidePoint[insidePointCount] = t.getVerticesVectors()[1];
insidePointCount++;
}else{
outsidePoint[outsidePointCount] = t.getVerticesVectors()[1];
outsidePointCount++;
}
if (d2 >= 0){
insidePoint[insidePointCount] = t.getVerticesVectors()[2];
insidePointCount++;
}else{
outsidePoint[outsidePointCount] = t.getVerticesVectors()[2];
}
}
//Triangle has 1 point still inside view, remove original triangle add new clipped triangle
if (insidePointCount == 1) {
t.dontDraw();
Vertex newVert1 = vectorIntersectPlane(planePos, planeNorm, insidePoint[0], outsidePoint[0]);
Vertex newVert2 = vectorIntersectPlane(planePos, planeNorm, insidePoint[0], outsidePoint[1]);
vertices.add(newVert1);
vertices.add(newVert2);
//Triangles are stored with vertex references instead of the actual vertex object.
Triangle temp = new Triangle(t.getVertKeys()[0], vertices.size() - 2, vertices.size() - 1, vertices);
temp.setColor(1,0,0, t.getBrightness(), t.getAlpha());
triangles.add(temp);
continue;
}
//Triangle has two points inside remove original add two new clipped triangles
if (insidePointCount == 2) {
t.dontDraw();
Vertex newVert1 = vectorIntersectPlane(planePos, planeNorm, insidePoint[0], outsidePoint[0]);
Vertex newVert2 = vectorIntersectPlane(planePos, planeNorm, insidePoint[1], outsidePoint[0]);
vertices.add(newVert1);
vertices.add(newVert2);
Triangle temp = new Triangle(t.getVertKeys()[0], t.getVertKeys()[1], vertices.size() - 1, vertices);
temp.setColor(0, 1, 0, t.getBrightness(), t.getAlpha());
triangles.add(temp);
temp = new Triangle(t.getVertKeys()[0], t.getVertKeys()[1], vertices.size() - 2, vertices);
temp.setColor(0, 0, 1, t.getBrightness(), t.getAlpha());
triangles.add(temp);
continue;
}
}
}
I figured out the problem, The new clipped triangles were not being given the correct vertex references. they were just being given the first vertex of the triangle irregardless of if that was inside the view or not.
I have been doing a small little project using Processing, and the effect I wanted to achieve was a kind of "mountains" forming and moving, using Perlin Noise with the noise() function, with 2 parameters.
I was originally using a image for the background, but for illustrational purposes, I made the background black, and it's basically the same effect.
My issue is that I want to have a "history" of the mountains because they should fade away after some time, and so I made a history of PShapes, and draw the history and update it each frame.
Updating it is no issue, but drawing the PShapes seems to take a lot of time, reducing the frame rate from 60 to 10 when the length of the history is 100 elements.
Below is the code I used :
float noise_y = 0;
float noise_increment = 0.01;
// increment x in the loop by this amount instead of 1
// makes the drawing faster, since the PShapes have less vertices
// however, mountains look sharper, not as smooth
// bigger inc = better fps
final int xInc = 1;
// maximum length of the array
// bigger = less frames :(
final int arrLen = 100;
int lastIndex = 0;
PShape[] history = new PShape[arrLen];
boolean full = false;
// use this to add shapes in the history
PShape aux;
void setup() {
size(1280, 720);
}
void draw() {
background(0);
// create PShape object
aux = createShape();
aux.beginShape();
aux.noFill();
aux.stroke(255);
aux.strokeWeight(0.5);
for (float x = 0; x < width + xInc; x = x + xInc) {
float noise = noise(x / 150, noise_y) ;
// get the actual y coordinate
float y = map(noise, 0, 1, height / 2, 0);
// create vertex of shape at x, y
aux.vertex(x, y);
}
aux.endShape();
// push the current one in the history
history[lastIndex++] = aux;
// if it reached the maximum length, start it over ( kinda works like a queue )
if (lastIndex == arrLen) {
lastIndex = 0;
full = true;
}
// draw the history
// this part takes the MOST TIME to draw, need to fix it.
// without it is running at 60 FPS, with it goes as low as 10 FPS
if (full) {
for (int i = 0; i < arrLen; i++) {
shape(history[i]);
}
} else {
for (int i = 0; i < lastIndex; i++) {
shape(history[i]);
}
}
noise_y = noise_y - noise_increment;
println(frameRate);
}
I have tried to use different ways of rendering the "mountains" : I tried writing my own class of a curve and draw lines that link the points, but I get the same performance. I tried grouping the PShapes into a PShape group object like
PShape p = new PShape(GROUP);
p.addChild(someShape);
and I got the same performance.
I was thinking of using multiple threads to render each shape individually, but after doing some research, there's only one thread that is responsible with rendering - the Animation Thread, so that won't do me any good, either.
I really want to finish this, it seems really simple but I can't figure it out.
One possible solution would be, not to draw all the generated shapes, but to draw only the new shape.
To "see" the shapes of the previous frames, the scene can't be cleared at the begin of the frame, of course.
Since the scene is never cleared, this would cause, that the entire view is covered, by shapes over time. But if the scene would be slightly faded out at the begin of a new frame, instead of clearing it, then the "older" shapes would get darker and darker by time. This gives a feeling as the "older" frames would drift away into the depth by time.
Clear the background at the initlization:
void setup() {
size(1280, 720);
background(0);
}
Create the scene with the fade effect:
void draw() {
// "fade" the entire view
blendMode(DIFFERENCE);
fill(1, 1, 1, 255);
rect(0, 0, width, height);
blendMode(ADD);
// create PShape object
aux = createShape();
aux.beginShape();
aux.stroke(255);
aux.strokeWeight(0.5);
aux.noFill();
for (float x = 0; x < width + xInc; x = x + xInc) {
float noise = noise(x / 150, noise_y) ;
// get the actual y coordinate
float y = map(noise, 0, 1, height / 2, 0);
// create vertex of shape at x, y
aux.vertex(x, y);
}
aux.endShape();
// push the current one in the history
int currentIndex = lastIndex;
history[lastIndex++] = aux;
if (lastIndex == arrLen)
lastIndex = 0;
// draw the newes shape
shape(history[currentIndex]);
noise_y = noise_y - noise_increment;
println(frameRate, full ? arrLen : lastIndex);
}
See the preview:
I am making a minecraft mod that implements a new system of "energy" for the player. There are various ways to acquire this energy and I want it to display the player's amount of energy onto the screen. My plan for this is to make a GUI (with OpenGL, as minecraft uses) that uses a file called "energybar.png":
to print numbers. This is the code I have for the method that will do as described.
#SubscribeEvent
public void onGUIRenderEvent(RenderGameOverlayEvent event){
if(event.isCancelable() || event.type != RenderGameOverlayEvent.ElementType.EXPERIENCE)
{
return;
}
int xPos = 10;
int yPos = 10;
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
GL11.glDisable(GL11.GL_LIGHTING);
mc.renderEngine.bindTexture(energybar);
String energyString = Integer.toString(Energy.PlayerTotalEnergy);
for(int i=0; i < energyString.length(); i++) {
LogHelper.info("Energy: " + energyString);
drawTexturedModalRect(xPos, yPos, (Energy.PlayerTotalEnergy / (int)Math.pow(10, i))*16, 0, 16, 16);
xPos += 16;
}
}
Each number in the photo is spaced out so it should be in its respective 16 pixels (ie: 0 is x positions 0-16, 1 is x positions 17-32, etc). The photo is 256x256 as defined by the standards of minecraft's GUI system. This is the layout of the method to draw a picture:
void drawTexturedModalRect(int xPosition, int yPosition, int uPosition, int vPosition, int width, int height)
The problem I have with this is that the U Positions for the numbers i need to print onto the screen are not working right.
I have also tried passing:
energyString.substring(i, i)
to a method that takes the substring and converts it back to an integer and multiplies it by 16 to get the uPosition, but when I do the:
String energyString = Integer.toString(Energy.PlayerTotalEnergy);
the Integer.toString() and also String.valueOf() methods have trouble with zeros. For example if Energy.PlayerTotalEnergy was just 0, they would not return a string "0", they just return "".
If someone could help me figure out why I can't get this to work or come up with a better idea of how I can use Minecraft and OpenGL to print this number onto my screen. The reason I'm not just printing it as a number is because I want to keep the red numbers as they look.
This is more of a guess.
It seems to me that if Energy.PlayerTotalEnergy was, let's say, 327, then your uPosition will be:
i=0: u= 327*16
i=1: u= 32*16
i=2: u= 3*16
Did you mean for them to be 7*16, 2*16, and 3*16?
In that case you should mod them with 10:
drawTexturedModalRect(xPos, yPos, ( (Energy.PlayerTotalEnergy / (int)Math.pow(10, i))%10)*16, 0, 16, 16);
So I am making a simple game using LibGDX which involes a 150*150 hexagonal map, and various types of cell, rocky, clear etc.
Problem is when i load the map, my computer almost completely freezes up and any movement thats supposed to be fluid (character moving, button highlights) take 5+ seconds longer than they should.
Here's the relevant code:
public void render(float deltY){
Gdx.gl.glClearColor(255, 255, 255, 100);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act();
polygonSpriteBatch.begin();
for (int j = 0; j < 150; j++) {
for (int i = 0; i < 150; i++) {
offset = i%2 == 0 ? multipleX/2 : 0;
if (mc.getMap().getRow(i).getTile(j).getTileType().equals(TileType.Rocky)) {
drawCell(Color.BLACK, j, i);}
if (mc.getMap().getRow(i).getTile(j).getTileType().equals(TileType.Clear)) {
drawCell(Color.LIGHT_GRAY, j, i);}
}
}
polygonSpriteBatch.end();
stage.draw();
}
private void drawCell(Color color, int x, int y) {
polySprite = new PolygonSprite(makePoints(color));
polySprite.setX(mc.getMap().getRow(y).getTile(x).getTilePosition().get_x() * multipleX + offset);
polySprite.setY(mc.getMap().getRow(y).getTile(x).getTilePosition().get_y() * multipleY);
polySprite.draw(polygonSpriteBatch);
}
public PolygonRegion makePoints(Color color){
side = 5;
h = CalculateH(side);
r = CalculateR(side);
multipleX = (float)Math.sqrt(3)*side;
multipleY = side+(side/2);
float[] points = { // vertices
x, y,
x+r, y+h,
x+r, y+side+h,
x,y+side+h+h,
x-r, y+side+h,
x-r, y+h};
return new PolygonRegion(new TextureRegion(getTexture(color)),points
, new short[] { //4 triangles using vertices to make hexagon
0, 1, 5,
1, 4, 2,
5, 1, 4,
2, 3, 4});
}
public Texture getTexture(Color color){
Pixmap pix = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
pix.setColor(color);
pix.fill();
textureSolid = new Texture(pix);
return textureSolid;
}
I'm new to coding and LibGDX so there's probably something stupid i'm doing. Is there any way to render the map once and only redraw the polygons if they change?
Thanks
Looking at your code, you are computing a square root for each cell, for each rendering pass.
So your code currently involves more than 22500 square root operations for each frame you render and is creating as many objects, that's quite a lot !
You should compute the points for your hexagons only once.