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);
Related
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 trying to make holdable items for my slick2d game, I figured a good way to do this would be to have a hand on the player, to do this i have 1 pixel a unique colour, allowing me to locate that colour, and the x +y.
It worked perfectly until i tried to scale up the image and i get this crazy out of bounds exeception.
this is my code to find the x and y:
public int[] getLocation(Image i, Color c){
Image scaled = i.getScaledCopy(getWidth(), getHeight());
for(int x = 0; x < scaled.getWidth(); x++){
for(int y = 0; y < scaled.getHeight(); y++){
if(scaled.getColor(x, y).r == c.r && scaled.getColor(x, y).g == c.g && scaled.getColor(x, y).b == c.b){
int[] xy = {x,y};
return xy;
}
}
}
return null;
}
and this is how i use it
float x = (float) (getLocation(walkLeft.getCurrentFrame(), new Color(1, 1, 1))[0] + getX());
float y = (float) (getLocation(walkLeft.getCurrentFrame(), new Color(1, 1, 1))[1] + getY());
g.fillRect(x, y, 2, 2);
the exception is:
java.lang.ArrayIndexOutOfBoundsException: 16384
and it leads me back to this line:
if(i.getColor(x, y).r == c.r && i.getColor(x, y).g == c.g && i.getColor(x, y).b == c.b){
in the getLocation method..
I have a feeling its dead easy, yet its stumped me. Thanks to anyone to responds.
The loop(s) in getLocation loop over the dimensions of a 2x scaled copy of the Image, but then attempt to access the pixels of the original. Given the original is half the size, when you are half-way through the loop you will be out of bounds of the image dimensions. Either:
Don't scale the Image
If you must scale the Image, check the pixel value of the scaled image rather than the original.
As an aside, the code posted contains redundant calls...a) in getLocation if you are going to scale, consider scaling the image once rather than placing that code within the loop itself b) no need to call getLocation twice with the same parameters. Call it once and just use the returned array
I am currently working on a game that requires a hexagon grid to be rendered in order to provide a place for the game to take place. I am using offset coordinates to store the hex grid in memory. When I attempt to render the grid, I get a result like the following with space between the hexes. I want the hexes to perfectly align so that all of the neccessary are at the exact same points, and so that their lines overlap and there is no space between the hexes
The coordinates inside each hex represent the offset coordinates of the hex.
I am using the distances shown in the following image (source) to decide where to render each hex.
The only StackExchange question I was able to find that addresses a problem that sounds like this one is here. It, however, only talks about a data structure to store the hexes in and not how to render them from said structure. I am attempting to render them as described here, however, I get the buggy result shown above.
Here is the portion of my code that handles rendering (it should be easy to infer what the various custom methods do, if there is any confusion please let me know):
#Override
public void paint(Graphics g){
int quarterHexSize = 32; //using non static final member variables because I want to make this dynamically determine the largest size of the hexagons that will fit on the screen later
int halfHexSize = quarterHexSize * 2;
int hexSize = halfHexSize * 2;
int cx, cy, renderX, renderY;
g.setColor(Color.DARK_GRAY);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.WHITE);
for(cx=0;cx<5;cx++){
for(cy=0;cy<5;cy++){
Hex hex = board.getHexAt(cx, cy);
if(hex != null){
renderX = cx * 2; //multiplying by 2 rather than using floats to represent half offset to simplify code and because IIRC integers are faster, I got the same error when using floats
renderY = cy * 2;
if(renderY % 4 != 0){
renderX++;
}
//converts the somewhat arbitrary units into the actual display size units using the values from the image in the question
renderX *= hexSize;
renderY *= quarterHexSize * 3f;
//the numbers are divided by 2 to make up for the offset
renderX /= 2;
renderY /= 2;
//64 is added to both dimensions to shift the grid inside the window
renderX += 64;
renderY += 64;
drawHex(new RenderPoint(renderX, renderY), halfHexSize, g);
g.drawString(cx + ", " + cy, renderX, renderY);
}
}
}
}
private void drawHex(RenderPoint center, int hexSize, Graphics g){
drawHexLine(center, hexSize, 0, 1, g);
drawHexLine(center, hexSize, 1, 2, g);
drawHexLine(center, hexSize, 2, 3, g);
drawHexLine(center, hexSize, 3, 4, g);
drawHexLine(center, hexSize, 4, 5, g);
drawHexLine(center, hexSize, 5, 0, g);
}
private void drawHexLine(RenderPoint center, int hexSize, int firstCornerNum, int secondCornerNum, Graphics g){
RenderPoint firstCornerNumHexPoint = getHexCorner(center, hexSize, firstCornerNum);
RenderPoint secondCornerNumHexPoint = getHexCorner(center, hexSize, secondCornerNum);
g.drawLine(
firstCornerNumHexPoint.getX(), firstCornerNumHexPoint.getY(),
secondCornerNumHexPoint.getX(), secondCornerNumHexPoint.getY()
);
//g.drawString(String.valueOf(firstCornerNum), firstCornerNumHexPoint.getX(), firstCornerNumHexPoint.getY());
}
private RenderPoint getHexCorner(RenderPoint center, int hexSize, int cornerNum){
return RenderPoint.doublesToInts( //simply rounds the provided doubles and creates a RenderPoint object with these new rounded values
center.getX() + hexSize * Math.sin(cornerNum * 60 * 0.0174533), //decimal number converts from degrees to radians
center.getY() + hexSize * Math.cos(cornerNum * 60 * 0.0174533)
);
}
I have determined what the error was. I had missed a specific detail in the webpage when I assumed that the diagram I posted in the question completely explained the placement of the hexagons.
I have changed the variable renderX to:
renderX *= Math.round(HALF_SQRT_THREE * hexSize);
HALF_SQRT_THREE is a constant I defined in the variable to avoid recalculating it each time a hexagon is rendered. It is defined as Math.sqrt(3)/2.0.
Here is the quote from the webpage:
The width of a hexagon is width = sqrt(3)/2 * height. The horizontal distance between adjacent hexes is horiz = width.
I was making a random line generator for fun, and when I run the program, the "Y (End): " string looks very strange. The numbers are displayed as rectangular symbols, that have no meaning.
Here is my code:
public class LineFractal extends Applet {
Random random = new Random();
int width = 640;
int height = 640;
int x = 1000;
int endy1 = random.nextInt(width/2);
int endx1 = random.nextInt(width/2);
int starty1 = random.nextInt(width/2);
int startx1 = random.nextInt(width/2);
int space = random.nextInt(25);
public void init() {
setSize(width, height);
Frame c = (Frame)this.getParent().getParent();
c.setTitle("Line Generator");
}
public void paint(Graphics g) {
while (x > 0) {
x -= 1;
g.drawLine(startx1, starty1, endx1, endy1);
g.drawString("Space: " + space, width-100, height-10);
g.drawString("Y (End): " + endy1, width-100, height-20);
endy1 += space;
endx1 -= space;
}
}
}
Why does it do this?
EDIT: Just tried the program again, and the y was actually displayed correctly for once. But then I ran it again, and only the last number could be seen. The last time I ran it, there were no numbers that were readable...
You seem to be printing the string Y (End) and then a number over the top of itself 1000 times. Obviously that's OK for the text, but the number part is going to look messy very quickly! Imagine writing 0 1 2 3 4 5 6 7 8 9 all on top of each other.
You probably mean to draw a white (or whatever background colour) filled rectangle to the area where the strings are to be printed, just to clear what's there already.
I fixed it by putting the two drawString() outside the while loop.
Some Context:
I am creating an Android game which draws a maze onto a canvas against a background image, this uses a square block and therefore the maze is always automtically zoomed in to a 5 x 5 square, of a maze that may be 20 x 20. The maze is drawn by simply running through a set of for loops and then drawing lines in the relevent places. This is all working perfectly, however I am having an issue with getting my onDraw to work smoothly. This is occuring due to the fact that everytime I invalidate I have to re-run the for look and run various if statements to check positions (unfortunetly this process cannot be improved).
The Question:
I am looking to re-write the way my maze is drawn onto the canvas, below are the 3 main features I need to implement:
1 - Draw the whole maze onto the canvas (this is easy enough)
2 - Zoom in on the maze so only a 5 x 5 is shown
3 - Move the character (who is always centered on the screen) and draw the next seciton of the maze
Now as mentioned above drawing the whole maze is easy enough and will make the onDraw signifcatly quicker as their is no need to run the for loop on invaldate, it can be done once when the level is first loaded up.
In terms of point 2 & 3, the way I see this working is the charcacter to be drawn in the middle of the canvas then a 2d birdseye view camera to be attached / linked to the characters movement. This camera would also need to be zoomed in to an extent that it only displays a 5 x 5 of the overall maze grid. Then as the charcater moves the camera moves with the character and displays the next section of the maze which has already been drawn. I have tried a few things and done some reasearch however I have never worked with canvas before and no idea really where to start and if this is even possible.
So to sum up the main part of the question is whether their is a way to link a birdseye view camera to a character which is zoomed in and moves with the character image.
Below is a snippet as to how the maze is currently drawn, using 2 sets of for loops checking against 2 sets of boolean arrays[][], which simply store the vertical and horixaonl lines to be drawn.
#Override
protected void onDraw(Canvas canvas)
{
canvas.drawRect(0, 0, width, 100, background);
RectBackground.set(0,0, FullScreenWidth, FullScreenWidth);
canvas.drawBitmap(gameBackground, null, RectBackground, null);
for(int i = 0; i < 5; i++)
{
for(int j = 0; j < 5; j++)
{
float x = j * totalCellWidth;
float y = i * totalCellHeight;
indexY = i + currentY;
indexX = j + currentX;
// Draw Verticle line (y axis)
if (indexY < vLength && indexX < vLines[indexY].length && vLines[indexY][indexX])
{
RectOuterBackground.set((int)x + (int)cellWidth, (int)y, (int)x + (int)cellWidth + 15, (int)y + (int)cellHeight + 15);
canvas.drawBitmap(walls, null, RectOuterBackground, null);
}
// Draws Horizontal lines (x axis)
if (indexY < hLength && indexX < hLines[indexY].length && hLines[indexY][indexX])
{
RectOuterBackground.set((int)x, (int)y + (int)cellHeight,(int)x + (int)cellWidth + 15,(int)y + (int)cellHeight + 15);
canvas.drawBitmap(walls, null, RectOuterBackground, null);
}
}
}
}
To make the drawing faster, you can double buffer the canvas by drawing directly into a bitmap and flashing the bitmap into the canvas like this. It's very fast.
private void init()
{
//variables below are class-wide variables
b = Bitmap.createBitmap(cwidth, cheight, Bitmap.Config.ARGB_8888);
bitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bitmapPaint.setStyle(Paint.Style.STROKE);
bitmapPaint.setStrokeWidth(3);
myPaint.setColor(0xff000000);
myCanvas = new Canvas(b);
}
protected void onDraw(Canvas c)
{
super.onDraw(c);
c.drawBitmap(b, 0, 0, myPaint);
for(int i = 0; i < number lines; i++)
{
//draw here using myPath
}
if(b != null)
c.drawBitmap(b, 0, 0, myPaint);
myCanvas.drawPath(myPath, bitmapPaint);
}
Then, to "move around" I would suggest using a crop box. What this means is that a 1:1 scale, the image is larger than the viewport displayed on the screen. Really what's happening when someone "moves" is the bitmap is being moved beneath the crop box.
You could use BitmapRegionDecoder and display only the region the character is in (this might be slowish) or by using
public void drawBitmap (Bitmap bitmap, Rect src, RectF dst, Paint paint)
The src parameter here allows you to specify a small region of the bitmap to display. It is effectively a crop box.
To zoom in and out you need to multiply the coordinates by a number( zoom factor)
int x1 = (int)x + (int)cellWidth;
int y1 = (int)y;
int x2 = (int)x + (int)cellWidth + 15;
int y2 = (int)y + (int)cellHeight + 15;
RectOuterBackground.set(x1*zoom, y1*zoom, x2*zoom, y2*zoom);
note that zoom must be a floating point number, using zoom=2 that makes everything twice as big.
if you want to put the camera on top of the cell (xc, yc) you need to do this:
RectOuterBackground.set( (x1-xc)*zoom, (y1-yc)*zoom, (x2-xc)*zoom, (y2-yc)*zoom);
Try first drawing the whole maze, and soon you will figure out how to only draw the bit of the maze that is only inside the screen
I hope this helps and let me know if you have any questions :)