Okay here is the problem:
i do have a list of Objects that does need to be updated by the time since the last frame
the list need to be ordered by the y koordinate of the Object before they get draw
Current i do this with a collection.sort and a simple comperator:
figureComperator = new Comparator<Actor>() {
#Override
public int compare(Actor o1, Actor o2) {
return (int) o2.getY() - (int) o1.getY();
}
};
The render does look like this:
#Override
public void render(float delta) {
// clearing
Gdx.gl.glClearColor(0, 0, 0, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// act the character act in front
this.character.myAct(delta);
updateGameCam();
if (this.status == GameStatus.GAME) {
// just monster act now
this.figureStage.act();
}
this.figureStage.getActors().sort(figureComperator); // sort figures
// render background
this.map.drawBackground(); // draw background
// draw figures inclusive character
this.figureStage.draw(); //draw all figures
this.map.drawForeground(); // foreground
// render game HUD and act gameHud
this.gameHud.act(delta);
this.gameHud.draw();
}
So i am looking for a better way to improve the speed of this.
I do came across the idea of let the actors sort itself inside of the list(figureStage) while i do update those. But as i do the update forall with the this.figureStage.act(); i couldnt do it that easy.
So my question is, if there is any solution without having the complexity of an sorting algorithm to always have those objects sorted by its position?
You can store your Objects in an array (or list) of HEIGHT elements in size, where HEIGHT is vertical size of your world. Each element in this list is a list of objects with the corresponding Y coordinate.
When an object moves up or down it should be removed from one list and added to another according to its new Y coordinate.
This way you can just iterate the array and draw all objects in inner lists.
If Y coordinate is not integer you can use integer part of it.
Hope this helps.
With the help of #Joni i started using a differend algorithm to sort the Actors. It does increased the sorting speed by around 2-3 Times just by using an insertionsort.
public void sortList(Array<Actor> array) {
// double starttime = System.nanoTime();
for (int i = 1; i < array.size; i++) {
Actor temp = array.get(i);
int j = i - 1;
while (j >= 0 && array.get(j).getY() < temp.getY()) {
array.set(j + 1, array.get(j));
j--;
}
array.set(j + 1, temp);
}
// System.out.println("Time taken: " + (System.nanoTime() - starttime));
}
Average ns of the Collection.sort was ~8700. Insertion is around 3300!
Related
I am about to learn java right now (I came from C++), and I am trying myself in GUI-Programming.
My goal is to create a playable version of "conway's game of life" (which is a good beginner project i think).
What i have managed to do is, that i stored all of my cells in a 2d array (Here is my gode for that)
public class Panel extends JPanel
{
private int PanelX = 1777, PanelY = 1000;
public boolean[][] grid = new boolean[192][108];
public Panel()
{
this.setBackground(Color.black);
this.setSize(PanelX, PanelY);
this.setVisible(true);
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.green);
prepareGrid(); // sets the entire index of grid[][] to zero for start and reset
drawGrid(g2d); // draws the grid
}
private void prepareGrid()
{
for (int i = 0; i<191; i++)
{
for (int t = 0; t<107; t++)
{
grid[i][t] = false;
}
}
}
private void drawGrid(Graphics g)
{
for (int i=0; i<=191; i++)
{
for (int t=0; t<=107; t++)
{
if (grid[i][t] == false)
{
g.drawRect(i*10, t*10, 10, 10);
}
if(grid[i][t] == true)
{
g.fillRect(i*10, t*10, 10, 10);
}
}
}
}
}
So what it does it creates an 2 dimensional array which stores all cells as either false = no cell, or true = cell. When an array index (for example grid[100][100] is true, it draws a filled rectangle on that position as a "living cell".
To implement the game and the rules of the game now, I need a way to access all the neighbor positions of an index of that 2d array, but I do not know how to to that. Could anyone help me with that ? :)
-> And if you have major optimizations for my code, feel free to write them as well.
Adding to markspace's answer, it is important to ensure that the counting of alive cells does not interfere with their updating (letting cells be born or die) in the same generation. In other words: If you count 3 neighbors of a dead cell, you must not immediately set it to alive, because it must still be counted as dead for its other neighbors.
You could, for example, first count the neighbors for every cell and then, in a second nested loop, update every cell according to its previously counted number of neighbors. But you can do better with a more elaborate algorithm: you do not absolutely need an array of counters of the same size as your grid.
It's just counting, it's not really hard. Use two loops to count all eight adjacent cells, skip the middle one when you find that cell.
/**
* Returns the number of "cells" that are alive and adjacent to
* the cell at the given X,Y coordinates.
*
*/
public int aliveAdjacent( int x, int y ) {
int count = 0;
for( int xd = -1; xd <= 1; xd++ )
for( int yd = -1; yd <= 1; yd++ ) {
if( xd == 0 && yd == 0 ) continue;
if( isAlive( wrap(x+xd, width), wrap(y+yd,heigth) ) ) count++;
}
return count;
}
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'm making a game engine, and I want to have a rope object in the game. I've created the rope, and it behaves perfectly, except that if I apply a force to the bottom of the rope (like attaching the player to the rope and moving the player), the force doesn't propagate to the rest of the rope.
This is what the rope looks like, even when I try to move the player:
I would like the rope to move along with the player, but keep the player attached to the bottom of the rope.
This is the update method from the Rope class
public void update() {
for (int i = 0; i < segments.size(); i++) {
RopeSegment previous = null;
if (i != 0) {
previous = segments.get(i - 1);
}
final RopeSegment seg = segments.get(i);
// do collision detection
seg.update();
// if we are not the first (position locked) segment
if (previous != null) {
// if we are out of range of the previous segment
if (seg.getCenter().toPoint().distanceSq(previous.getCenter().toPoint()) > MAX_DIST * MAX_DIST) {
// pull us back in
final Vec2D previousCenter = previous.getPosition();
seg.applyForce(previousCenter.subtract(seg.getPosition()).multiply(0.01));
seg.setPosition(previousCenter.add(seg.getPosition().subtract(previousCenter).unit()
.multiply(MAX_DIST)));
}
}
}
// lock position of first segment
segments.get(0).setPosition(getLockposition());
segments.get(0).setVelocity(new Vec2D(0, 0));
}
and this is the relevant code from the update method of the Player class
if (rope != null) {
if (rope.getLockposition().toPoint().distanceSq(getCenter().toPoint()) > Rope.MAX_DIST * Rope.MAX_DIST) {
setCenter(rope.getLastSegment().getCenter());
}
}
If I were you: I would find the number of rope segments (Depending on height), there are between the player and the top of the screen (Y : 0px); thereby, allowing you to constantly update an integer; or appropriate size variable, with how many segments to draw. Then, every time you redraw, you can make the rope above the player. This would entitle deleting most of what you have, but I believe it to be a bit more efficient. If you want to make the rope look more 'Rope Like', then you could use an ArrayList, updated every time you go to draw, which will have a class defining position and a Vec2D which holds velocity, which will be used to increase the x position of the rope. e.g.
List<Rope> ropes = new ArrayList<>(); //Fill This With Ropes. . . //First is The Farthest Up!
class Rope {
int x, y;
short velocity;
//Constructor
}
void updateRopes(int playerX) {
for(Rope r : ropes)
r.x += (r.velocity < (playerX - r.x)) ? r.velocity : (playerX - r.x); //You Can Make a Variable Which Holds (playerX - r.x) For Efficiency, or Keep For RAM
}
void playerMove() {
int yDec = ropes.size() / 5; //5 Is the Presumable Speed of The Character. . .
int tempVeloc = 5;
for(int i = 0; i < ropes.size(); i++)
if((i % yDec) == 0 & i != 0) tempVeloc -= 1;
//The Other Player-Move Based Code. . .
}
EDIT:
I believe your problem is that you are not actually editing the classes inside of the array, you are making a constant copy of them; thereby not changing the object because you have not made a pointer to them. . .
What you should do is this:
RopeSegment seg = segments.get(i - 1);
I'm relatively new to programming, and i'm trying to create a program that creates a purple ball where i click that moves to the right until it is off the screen, where i can have unlimited balls on the screen at once. I've made a program that does this, but i can only have one on the screen at once, if i click a second time, the first ball disappears and is replaced with a new one. Oh, and when i click a second time, the ball doesn't start where the cursor is, it starts from where the last ball was on the X-Axis.
Help please!
Here's the code:
int moveX, moveY;
void setup() {
background(255);
moveY = 200;
moveX = 0;
size(500,400);
}
void mouseClicked() {
moveY = mouseY;
moveX++;
}
void draw() {
if (moveX >= 1){
background(255);
fill(255, 0, 255);
ellipse(moveX, moveY, 40, 40);
moveX++;
}
}
As donfuxx suggests, giving each ball it's own coordinates.
One way to do this is using an array to store multiple values(coordinates).
To do this you need to get familiar with for loops and arrays.
They may look scary at first, but once you get the hang of them they're quite easy.
Wherever you can think of a situation where repetition is needed, you can use a for loop to make your life easier.
For loop have the following syntax:
for keyword (3 elements: a start point,an end point(condition) and an increment,(separated by the ; character)
Let's say you want to move from a(0) to b(10) one step at a time:
for(int currentPos = 0 ; currentPos < 10; currentPos++){
println("step: " + currentPos);
}
If you can walk, you can also skip :)
for(int currentPos = 0 ; currentPos < 10; currentPos+=2){
println("step: " + currentPos);
}
even backwards if you want:
for(int currentPos = 10 ; currentPos > 0; currentPos--){
println("step: " + currentPos);
}
This is very useful when traversing all sort of data(coordinates of a ball in a scene, etc.)
How do you organize your data ? You place it in a list or array.
An array contains elements of the same type and has a set length.
The syntax to declare an array is like so:
ObjectType[] nameOfArray;
and you can initialize an empty array:
int[] fiveNumbers = new int[5];//new keyword then the data type and length in sq.brackets
or you can initialize the array with values:
String[] words = {"ini","mini","miny","moe"};
You access elements in an array using square brackets and the index of the object in the list you want to access. Arrays have a length property so you can easily count objects.
background(255);
String[] words = {"ini","mini","miny","moe"};
for(int i = 0 ; i < words.length; i++){
fill(map(i,0,words.length, 0,255));
text(words[i],10,10*(i+1));
}
Now back to your original question.
Here is your code using for loops and arrays:
int ballSize = 40;
int maxBalls = 100;//maximum number of balls on screen
int screenBalls = 0;//number of balls to update
int[] ballsX = new int[maxBalls];//initialize an empty list/array of x coordinates
int[] ballsY = new int[maxBalls];//...and y coordinates
void setup() {
size(500, 400);
fill(255, 0, 255);
}
void mouseClicked() {
if (screenBalls < maxBalls) {//if still have room in our arrays for new ball coordinates
ballsX[screenBalls] = mouseX;//add the current mouse coordinates(x,y)
ballsY[screenBalls] = mouseY;//to the coordinate arrays at the current ball index
screenBalls++;//increment the ball index
}
}
void draw() {
println(screenBalls);
background(255);
for (int i = 0 ; i < screenBalls; i++) {//start counting from 0 to how many balls are on screen
ballsX[i]++;//increment the x of each ball
if(ballsX[i]-ballSize/2 > width) ballsX[i] = -ballSize/2;//if a ball goes off screen on the right, place it back on screen on the left
ellipse(ballsX[i], ballsY[i], ballSize, ballSize);//display each ball
}
}
There are multiple ways to tackle this. Arrays have fixed size. If you don't want to be constrained by that you can use an ArrayList (sort of a variable size array). Later you might want to look into how you can make an object that can update and draw itself. Have fun!
I implemented different type of sorting (bubble, insertion, selection). Know I want to compare their implementations like the following for each sort (here's an example with the bubble sort) :
For example, here's my bubble sort :
private static int[] bubbleSort(int[] tabToSort) {
int [] tab = tabToSort.clone();
boolean tabSort = false;
while(!tabSort){
tabSort = true;
for(int i = 0; i < tab.length -1; i++){
if(tab[i]> tab[i+1]){
int temp = tab[i+1];
tab[i+1] = tab[i];
tab[i] = temp;
tabSort = false;
}
}
}
return tab;
}
I started the GUI and I placed 1000 random points on it and the line y=x :
#Override
public void paintComponent (Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLACK);
Dimension size = getSize();
Insets insets= getInsets();
int w = size.width - insets.left - insets.right;
int h = size.height - insets.top - insets.bottom;
g2d.drawLine(size.width ,0, 0, size.height);
Random r = new Random();
for (int i =0; i < 1000; i++) {
int x = Math.abs(r.nextInt()) % w;
int y = Math.abs(r.nextInt()) % h;
Point p = new Point(x, y);
g2d.drawLine(p.x, p.y, p.x, p.y);
}
}
Here's what I've done :
Now I'm stuck, I have no idea about how to start. Could anyone indicate me the steps/ hints to follow to implement that ?
Thanks :)
You must define what the points mean. Looking at the animation, it looks like the y axis represents a value, whilst the x axis represents the position in the array of that value.
In your paint method, you would then go through the list of items and paint a dot, with the x-point being the position in the array and the y-point being a position on the y-axis. Assuming the values are within a known range.
Also, remember that the y-axis in graphics starts with 0 at the top, so you may have to do some translation of values to coordinates (depending on how you want it to look).
The easiest way would be to convert your paint method into one that uses a predefined List of points as a parameter instead of random points. On each iteration of your sort method pass the sorted array into the paint method and repaint the dots.
You'll need to
Create an int[] array with random values as a member variable. Let's call the array data. You probably want to start with a fixed array size and range of 100 each. You can adjust the values to the window size later, when a simple version is working. It may be even better to stick to a fixed size and range and just scale to the space available in paintComponent, making the behavior independent of the window size.
Change paintComponent to loop over data. The loop index is your x value and data[x] determines the y value.
Test that the code still draws the initial random array. Don't care if it is in the uppler left corner only now, you can fix that when the animation is working.
You'll need to add some kind of sleep() call to the innermost loop of your sort method, so you get a chance to observe the steps. Otherwise, even bubblesort will be too fast to observe. I'd recommend to start with one second (parameter value 1000). Make it faster later when everything works.
Start the bubbleSort method in a new thread and make sure your component gets repainted with each step. This may be the most tricky part. Perhaps hand in the component to the bublleSort method (or make bubbleSort a non-static method of the component) and let it request a repaint() at each step (fortunately, this is one of the few thread safe methods in Swing).
Fine-tune your code: Scale the x and y coordinates by multiplying with the space available and then dividing by the array size or value range. Adjust the sleep time as needed. Add support for different sorting algorithms....
If any of the steps is unclear, add a comment.
I've done this for my bachelorthesis, I did it like this (it's not perfect, but it might help you):
(I removed some unimportant methods/functions from the code below. It's mainly to illustrate how I visualized it. You can replace the GRectangle class by a simple java.awt.Point for example.)
The initialization method gives you an example of how you can find the maximum and minimum value of the data so you know how to transform your datavalues => coordinates.
public class DotVisualisation extends Visualisation {
private ArrayList<GRectangle> m_points;
private Comparable[] m_data;
private Comparable m_maxValue;
private Comparable m_minValue;
private int MAX_HEIGHT; // max height in pixels of visualization
/**
* Creates a new DotVisualisation.<br>
* <br>
* This class is a runnable JComponent that will visualize data as a function.
* The visualisation will plot the data with X and Y coordinates on the window.
* The X coordinate of the point is index of the dataelement.
* The Y coordinate of the point is relative to the value of the dataelement.<br>
* <br>
* This visualisation should be used for medium and large arrays.
*
* #author David Nysten
*/
public DotVisualisation()
{
m_points = new ArrayList<GRectangle>();
MAX_HEIGHT = 150;
}
/**
* Returns the maximum supported dimension by this visualisation.
*
* #return The supported dimension.
*/
public static int getSupportedDimension()
{
return 1;
}
#Override
public Dimension getMaximumSize()
{
return getPreferredSize();
}
#Override
public Dimension getPreferredSize()
{
return new Dimension(m_points.size() + 2, MAX_HEIGHT + 6);
}
#Override
public Dimension getMinimumSize()
{
return getPreferredSize();
}
#Override
public void paintComponent(Graphics g)
{
for(int i = 0; i < m_points.size(); ++i)
m_points.get(i).paintComponent(g);
}
private void swap(int index, int index2) { // See below }
private void initialise()
{
findMinimum();
findMaximum();
m_points.clear();
double multiplier;
int x = 0, y = 0, h;
for(int i = 0; i < m_data.length; ++i)
{
if(m_data[i].compareTo(-1) <= 0)
h = 0;
else
{
Integer value = (Integer) m_data[i];
Integer min = (Integer) m_minValue;
Integer diff = (Integer) m_maxValue - min;
multiplier = MAX_HEIGHT / diff.doubleValue();
h = (int) ((value - min) * multiplier);
}
y = (int) (MAX_HEIGHT - h);
GRectangle r = new GRectangle(x, y, 1, 1); // 1, 1 = width and height
r.setColor(Color.BLACK);
m_points.add(r);
++x;
}
}
private void findMaximum()
{
Comparable max = null;
if(m_data.length > 0)
{
max = m_data[0];
for(int i = 1; i < m_data.length; ++i)
if(m_data[i].compareTo(max) > 0)
max = m_data[i];
}
m_maxValue = max;
}
private void findMinimum()
{
Comparable min = null;
if(m_data.length > 0)
{
min = m_data[0];
for(int i = 1; i < m_data.length; ++i)
if(m_data[i].compareTo(min) < 0)
min = m_data[i];
}
m_minValue = min;
}
}
Take this into account:
Visualizing integers between 0 and 150 on a height of 150 pixels is straightforward. Visualizing a set of integers between the values 565 and 3544545 on a height of 150 is a bit less so.
PS: The code uses the index of the element in the inputarray as the X-coordinate.
PS: The class keeps a reference to the inputarray (m_data variable) but that's ofcourse not needed, you only need it to initialize your points.
PS: My "Visualization" class which is extended by all visualizations, is basicly a JPanel.
PS: The code above is written for positive integers, so will probably need some extra coding to handle negative integers aswell ;).
Then to visualize the actions of the algorithm, I used the observer pattern. The algorithm, for example bubblesort, looked like this:
for(int i = 0; i < size(); ++i)
for(int j = 1; j < size(); ++j)
if(greaterThan(j - 1, j))
swap(j - 1, j);
Where the function swap was defined as follows (simplified version again):
protected void swap(int index1, int index2)
{
if(index1 != index2)
{
incrementSwap(); // counting swaps and visualizing counter
m_command.clear();
m_command.setAction(Action.SWAP);
m_command.addParameter(index1);
m_command.addParameter(index2);
setChanged();
notifyObservers(m_command);
E temp = m_data[index1];
m_data[index1] = m_data[index2];
m_data[index2] = temp;
}
}
Where I notified my observers (visualizations) that a swap occured on index1 and index2. The m_command variable is an instance of the Command-class (wrote it myself) which is just a wrapper for the information needed by the visualization. Which is: the action that occured and the relevant information (indices for a swap-action for example).
So in the visualization i swapped the GRectangles on those indices aswell as their X-coordinates;
private void swap(int index, int index2)
{
if(index == index2)
return;
GRectangle r1 = m_points.get(index);
GRectangle r2 = m_points.get(index2);
int tempX = r1.getX();
r1.setLocation(r2.getX(), r1.getY());
r2.setLocation(tempX, r2.getY());
m_points.set(index, r2);
m_points.set(index2, r1);
}
You can add lines like this:
try {
Thread.sleep(100);
} catch(InterruptedException ignore) {}
to let a thread sleep 100ms before continueing. This might come in handy if it's getting visualized too fast.
So for an array with random integers it might look like this:
And after sorting:
(Ofcourse it's not a straight line because the values in the inputarray were generated at random in this case)
So if you have to - like I had to - allow multiple algorithms to work with the same visualization, I can recommend you to separate the visualization class and the algorithm class and work with an observer pattern to let the visualization update whenever an action occurs (set, swap, ...).
And then you can create something like this for comparisons;
http://i445.photobucket.com/albums/qq179/ultddave/DotVisualizationMany_zps63269d2a.png
http://i445.photobucket.com/albums/qq179/ultddave/DotVisualizationMany2_zps65e96fa9.png
Good luck!