I have a third part JPanel that basically creates an ascii roguelike terminal for my game. Using this, I want to create a game, but I need to be able to draw on top of the panel with normal graphics. My problem is when I try to do this, the ascii looks fine, but the thing drawn on top flickers and sometimes doesn't show up.
This is my display code
public class Display {
private static final char TRANSPARENT_CHARACTER = ' ';
// Might break if the character is the same as TRANSPARENT_CHARACTER
private static final AsciiCharacterData HIGHLIGHTER = new AsciiCharacterData(
'H', new Color(255, 255, 0), new Color(255, 255, 0));
private final AsciiPanel displayPanel;
private final int widthInCharacters, heightInCharacters;
private final static int Z_LEVELS = DrawingLayer.values().length;
private final AsciiCharacterData[][][] characterMap;
public Display(final AsciiPanel panel) {
displayPanel = panel;
widthInCharacters = panel.getWidthInCharacters();
heightInCharacters = panel.getHeightInCharacters();
characterMap = new AsciiCharacterData[widthInCharacters][heightInCharacters][Z_LEVELS];
for (int x = 0; x < widthInCharacters; x++) {
for (int y = 0; y < heightInCharacters; y++) {
for (int z = 0; z < Z_LEVELS; z++) {
characterMap[x][y][z] = new AsciiCharacterData(
TRANSPARENT_CHARACTER,
displayPanel.getDefaultForegroundColor(),
displayPanel.getDefaultBackgroundColor());
}
}
}
}
public void setCharacterAt(final int x, final int y, final DrawingLayer z,
final AsciiCharacterData c) {
characterMap[x][y][z.layer] = c;
// if z is not the top level
if (z.layer != Z_LEVELS - 1) {
// check all levels above
for (int i = z.layer + 1; i < Z_LEVELS; i++) {
// if there is an opaque character
if (characterMap[x][y][i].character != TRANSPARENT_CHARACTER)
// we dont need to draw anything
return;
}
}
if (c.character == TRANSPARENT_CHARACTER) {
// loop through all characters under the transparent character
for (int i = z.layer - 1; i >= 0; i--) {
// if we find a non transparent character
if (characterMap[x][y][i].character != TRANSPARENT_CHARACTER) {
// display that one instead
displayPanel.write(characterMap[x][y][i].character, x, y,
characterMap[x][y][i].foregroundColor,
characterMap[x][y][i].backgroundColor);
return;
}
}
// if there were no non trasparent characters
displayPanel.write(TRANSPARENT_CHARACTER, x, y);
// if we are a highlighter, we draw the below character and then
// just draw on top
} else {
displayPanel.write(c.character, x, y, c.foregroundColor,
c.backgroundColor);
}
displayPanel.repaint();
}
public void highlightOnScreen(final int x, final int y) {
final Graphics g = displayPanel.getGraphics();
g.setColor(HIGHLIGHTER.backgroundColor);
g.fillRect(x * AsciiPanel.getCharWidth(),
y * AsciiPanel.getCharHeight(), AsciiPanel.getCharWidth(),
AsciiPanel.getCharHeight());
}
public AsciiCharacterData getCharacterAt(final int x, final int y,
final DrawingLayer z) {
return characterMap[x][y][z.layer];
}
public int getWidth() {
return widthInCharacters;
}
public int getHeight() {
return heightInCharacters;
}
}
This is the draw method in the GameMap class:
public void draw(final Display display) {
for (int x = getViewportX(); x < getViewportX() + viewportWidthInTiles; x++) {
for (int y = viewportY; y < viewportY + viewportHeightInTiles; y++) {
final char character = background[x][y].getCharacter();
final Color foreground = background[x][y].getForeground();
final Color backgroundColor = background[x][y].getBackground();
final AsciiCharacterData data = new AsciiCharacterData(
character, foreground, backgroundColor);
display.setCharacterAt(x - getViewportX(), y - viewportY,
background[x][y].getDrawingLayer(), data);
}
}
for (int i = 0; i < entities.size(); i++) {
final Entity e = entities.get(i);
final char character = e.getCharacter();
final Color foreground = e.getForeground();
final Color backgroundColor = e.getBackground();
final AsciiCharacterData data = new AsciiCharacterData(character,
foreground, backgroundColor);
display.setCharacterAt(e.getX() - getViewportX(), e.getY()
- viewportY, e.getDrawingLayer(), data);
}
}
and this is the draw method in the Game Class
#Override
public void draw() {
final int x = map.mapTileXToDisplayTileX(0);
final int y = map.mapTileYToDisplayTileY(0);
display.highlightOnScreen(x, y);
map.draw(display);
}
How can I make it so that it draws every frame and doesn't flicker?
See How to Use Root Panes.
It is the glass pane which is of interest:
Hidden, by default. If you make the glass pane visible, then it's like a sheet of glass over all the other parts of the root pane. It's completely transparent unless you implement the glass pane's paintComponent method so that it does something, ..
Related
i'm making a 2D tile based game and i was working with AABB collision algorithm
when i got stucked in this problem, i have a matrix of tile:
Tile[][] matrix = new Tile[WIDTH][HEIGHT];
interface Tile {
public boolean isSolid();
}
based on this matrix i want calculate the AABB pool
that is a simple list, defined here:
List<AABB> aabbPool = new ArrayList<AABB>();
class AABB {
private int x;
private int y;
private int w;
private int h;
// Getter and Setter //
}
i'm looking for an algorithm that is capable of iterate the tile matrix
and find the largest possible AABB-Rectangle of solid attached tiles in the matrix,
let me explain:
Grid = The matrix
White = Unsolid tile
Black = Solid Tile
Given this matrix the algorithm will create the aabb pool, like this
Red outline = Y preferred aabb
Green outline = X preferred aabb (Y is not possible)
Blue outline = XY group
At the end, i created this script for debug the algorithm
public class AABBDebugger {
// Public field just for debug
static class AABB {
public int xPosition;
public int yPosition;
public int width;
public int height;
}
// Public field just for debug
static class Tile {
public static final int SIZE = 10;
public boolean solid;
}
public static void main(String[] args) {
// Matrix size
int WIDTH = 50;
int HEIGHT = 50;
// Declaration of matrix and random initialization
Tile[][] matrix = new Tile[WIDTH][HEIGHT];
for (int xCoord = 0; xCoord < WIDTH; xCoord++) {
for (int yCoord = 0; yCoord < HEIGHT; yCoord++) {
matrix[xCoord][yCoord] = new Tile();
matrix[xCoord][yCoord].solid = Math.random() > 0.5;
}
}
// Inizialization of the collission pool
List<AABB> aabbPool = new ArrayList<AABB>();
// Magic method that create the collision pool
magicMethod(matrix, WIDTH, HEIGHT, aabbPool);
// Rendering of result
Canvas canvas = new Canvas();
canvas.setPreferredSize(new Dimension(WIDTH * Tile.SIZE, HEIGHT * Tile.SIZE));
JFrame frame = new JFrame();
frame.add(canvas);
frame.pack();
frame.setVisible(true);
while (!Thread.interrupted()) {
BufferStrategy bufferStrategy;
while ((bufferStrategy = canvas.getBufferStrategy()) == null) {
canvas.createBufferStrategy(2);
}
Graphics graphics = bufferStrategy.getDrawGraphics();
for (int xCoord = 0; xCoord < WIDTH; xCoord++) {
for (int yCoord = 0; yCoord < HEIGHT; yCoord++) {
graphics.setColor(matrix[xCoord][yCoord].solid ? Color.BLACK : Color.WHITE);
graphics.fillRect(xCoord * Tile.SIZE, yCoord * Tile.SIZE, Tile.SIZE, Tile.SIZE);
}
}
for (AABB aabb : aabbPool) {
graphics.setColor(Color.RED);
graphics.drawRect(aabb.xPosition, aabb.yPosition, aabb.width, aabb.height);
}
bufferStrategy.show();
graphics.dispose();
}
System.exit(0);
}
/*
* The algorithm that i'm looking for
* for cycle start from Y rather then X
*/
private static void magicMethod(Tile[][] matrix, int WIDTH, int HEIGHT, List<AABB> aabbPool) {
for (int yCoord = 0; yCoord < HEIGHT; yCoord++) {
AABB aabb = null;
for (int xCoord = 0; xCoord < WIDTH; xCoord++) {
if (matrix[xCoord][yCoord].solid) {
if (aabb == null) {
aabb = new AABB();
aabb.yPosition = yCoord * Tile.SIZE;
aabb.xPosition = xCoord * Tile.SIZE;
aabb.height = Tile.SIZE;
aabb.width = Tile.SIZE;
} else {
aabb.width += Tile.SIZE;
}
} else if (aabb != null) {
aabbPool.add(aabb);
aabb = null;
}
}
if (aabb != null) {
aabbPool.add(aabb);
aabb = null;
}
}
}
}
The script produce this result:
but isn't what i expect because this algorithm group the tiles
by y and is ok, but not by x when i can, like there
Finally (sorry for the long post) the algorithm must respect this rules:
Prefer group the tiles by Y
Group the tiles by X when is not possible by Y
Don't overlap existing group
Group all the tiles
You can still use your method with a slight change: Do not directly store single-tile AABB rectangles grouped by Y (those with width==Tile.SIZE); but store them in a temp HashMap: Map<Integer, List<Integer>>, where the key is the xCoord of each tile and the value yCoord is added to the list). This way you can track which tiles per xCoord have not been grouped already by Y. So, check if width!=Tile.SIZE before adding:
if (aabb.width != Tile.SIZE) {
aabbPool.add(aabb);
} else {
addToMap(xCoord,yCoord);
}
aabb = null;
The method addToMap can be implemented as follows:
private static void addToMap(Integer x, Integer y, Map<Integer, List<Integer>> xMap) {
if (xMap.containsKey(x)) {
xMap.get(x).add(y);
} else {
List<Integer> yList = new ArrayList<Integer>();
yList.add(y);
xMap.put(x, yList);
}
}
Then, call the following method as a last step of your magicMethod; it will either add single-tile AABB rectangles (not possible to be grouped anyway), or grouped by X (that are not grouped by Y already).
private static void groupByX(Map<Integer, List<Integer>> xMap, List<AABB> aabbPool) {
//for each X
for (Entry<Integer, List<Integer>> entry : xMap.entrySet()) {
List<Integer> curList = entry.getValue();
if (curList != null) {
int curY = curList.get(0); //current yCoord
int curH = 1; //current height
//for each Y
for (int i = 1; i < curList.size(); i++) {
if (curY + curH == curList.get(i)) {
curH++;
} else {
aabbPool.add(new AABB(entry.getKey()*Tile.SIZE, curY*Tile.SIZE, Tile.SIZE, curH*Tile.SIZE)); // *Tile.SIZE()
//reset
curY = curList.get(i);
curH = 1;
}
}
//add the last AABB
aabbPool.add(new AABB(entry.getKey()*Tile.SIZE, curY*Tile.SIZE, Tile.SIZE, curH*Tile.SIZE));
}
}
}
This is what you get:
I have a problem with a hexagonal grid. I found this code you can see below on Internet, so it's not mine. There are two public classes: hexgame which generates the grid and hexmech which draws and fills every single hexagon. What I'd like to do is basically insert an image into a specific hexagon, but I don't know how to code this and in which part of the classes I should put it. Am I thinking the wrong way?
Thank you very much for your help!
Hexgame
package hex;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class hexgame
{
private hexgame() {
initGame();
createAndShowGUI();
}
final static Color COLOURBACK = Color.WHITE;
final static Color COLOURCELL = Color.WHITE;
final static Color COLOURGRID = Color.BLACK;
final static Color COLOURONE = new Color(255,255,255,200);
final static Color COLOURONETXT = Color.BLUE;
final static Color COLOURTWO = new Color(0,0,0,200);
final static Color COLOURTWOTXT = new Color(255,100,255);
final static Color COLOURSAFE = Color.WHITE;
final static Color COLOURDANGEROUS = Color.LIGHT_GRAY;
final static int EMPTY = 0;
final static int UNKNOWN = -1;
final static int SAFE = 1;
final static int DANGEROUS = 2;
final static int CLICKED = 3;
final static int COLUMN_SIZE = 23;
final static int ROW_SIZE = 14;
final static int HEXSIZE = 45;
final static int BORDERS = 15;
int[][] board = new int[COLUMN_SIZE][ROW_SIZE];
void initGame(){
hexmech.setXYasVertex(false);
hexmech.setHeight(HEXSIZE);
hexmech.setBorders(BORDERS);
for (int i=0;i<COLUMN_SIZE;i++) {
for (int j=0;j<ROW_SIZE;j++) {
board[i][j]=EMPTY;
}
}
board[5][5] = SAFE;
board[5][6] = SAFE;
board[5][7] = SAFE;
board[6][5] = SAFE;
board [6][6] = SAFE;
board[4][4] = UNKNOWN;
}
private void createAndShowGUI()
{
DrawingPanel panel = new DrawingPanel();
JFrame frame = new JFrame("Hex Testing 4");
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
Container content = frame.getContentPane();
content.add(panel);
frame.setSize(825, 630);
frame.setResizable(true);
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
class DrawingPanel extends JPanel
{
public DrawingPanel()
{
setBackground(COLOURBACK);
MyMouseListener ml = new MyMouseListener();
addMouseListener(ml);
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setFont(new Font("TimesRoman", Font.PLAIN, 15));
super.paintComponent(g2);
for (int i=0;i<COLUMN_SIZE;i++) {
for (int j=0;j<ROW_SIZE;j++) {
if (board[i][j] != UNKNOWN)
hexmech.drawHex(i,j,g2);
}
}
for (int i=0;i<COLUMN_SIZE;i++) {
for (int j=0;j<ROW_SIZE;j++) {
if (board[i][j] != UNKNOWN)
hexmech.fillHex(i,j,board[i][j],g2);
}
}
}
class MyMouseListener extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
int x = e.getX();
int y = e.getY();
Point p = new Point( hexmech.pxtoHex(e.getX(),e.getY()) );
if (p.x < 0 || p.y < 0 || p.x >= COLUMN_SIZE || p.y >= ROW_SIZE) return;
board[p.x][p.y] = CLICKED;
repaint();
}
}
}
}
Hexmech
package hex;
import java.awt.*;
import javax.swing.*;
public class hexmech
{
#define HEXEAST 0
#define HEXSOUTHEAST 1
#define HEXSOUTHWEST 2
#define HEXWEST 3
#define HEXNORTHWEST 4
#define HEXNORTHEAST 5
public final static boolean orFLAT= true;
public final static boolean orPOINT= false;
public static boolean ORIENT= orFLAT;
public static boolean XYVertex=true;
private static int BORDERS=50
private static int s=0; // length of one side
private static int t=0; // short side of 30o triangle outside of each hex
private static int r=0; // radius of inscribed circle (centre to middle of each side). r= h/2
private static int h=0; // height. Distance between centres of two adjacent hexes. Distance between two opposite sides in a hex.
public static void setXYasVertex(boolean b) {
XYVertex=b;
}
public static void setBorders(int b){
BORDERS=b;
}
public static void setSide(int side) {
s=side;
t = (int) (s / 2); //t = s sin(30) = (int) CalculateH(s);
r = (int) (s * 0.8660254037844);
h=2*r;
}
public static void setHeight(int height) {
h = height;
r = h/2; // r = radius of inscribed circle
s = (int) (h / 1.73205); // s = (h/2)/cos(30)= (h/2) / (sqrt(3)/2) = h / sqrt(3)
t = (int) (r / 1.73205); // t = (h/2) tan30 = (h/2) 1/sqrt(3) = h / (2 sqrt(3)) = r / sqrt(3)
}
public static Polygon hex (int x0, int y0) {
int y = y0 + BORDERS;
int x = x0 + BORDERS;
if (s == 0 || h == 0) {
System.out.println("ERROR: size of hex has not been set");
return new Polygon();
}
int[] cx,cy;
if (XYVertex)
cx = new int[] {x,x+s,x+s+t,x+s,x,x-t}; //this is for the top left vertex being at x,y. Which means that some of the hex is cutoff.
else
cx = new int[] {x+t,x+s+t,x+s+t+t,x+s+t,x+t,x}; //this is for the whole hexagon to be below and to the right of this point
cy = new int[] {y,y,y+r,y+r+r,y+r+r,y+r};
return new Polygon(cx,cy,6);
}
public static void drawHex(int i, int j, Graphics2D g2) {
int x = i * (s+t);
int y = j * h + (i%2) * h/2;
Polygon poly = hex(x,y);
g2.setColor(hexgame.COLOURCELL);
//g2.fillPolygon(hexmech.hex(x,y));
g2.fillPolygon(poly);
g2.setColor(hexgame.COLOURGRID);
g2.drawString(String.format("%c;%d", 'A'+i, j+1), x+20, y+40);
g2.drawPolygon(poly);
}
public static void fillHex(int i, int j, int n, Graphics2D g2) {
char c='o';
int x = i * (s+t);
int y = j * h + (i%2) * h/2;
/*if (n < 0) {
g2.setColor(hexgame.COLOURONE);
g2.fillPolygon(hex(x,y));
g2.setColor(hexgame.COLOURONETXT);
c = (char)(-n);
g2.drawString(""+c, x+r+BORDERS, y+r+BORDERS+4); //FIXME: handle XYVertex
//g2.drawString(x+","+y, x+r+BORDERS, y+r+BORDERS+4);
}
if (n > 0) {
g2.setColor(hexgame.COLOURTWO);
g2.fillPolygon(hex(x,y));
g2.setColor(hexgame.COLOURTWOTXT);
c = (char)n;
if (n==3) {
g2.setColor(hexgame.COLOURTWO);
g2.fillPolygon(hex(x,y));
g2.setColor(hexgame.COLOURTWOTXT);
}
}
public static Point pxtoHex(int mx, int my) {
Point p = new Point(-1,-1);
//correction for BORDERS and XYVertex
mx -= BORDERS;
my -= BORDERS;
if (XYVertex) mx += t;
int x = (int) (mx / (s+t));
int y = (int) ((my - (x%2)*r)/h);
int dx = mx - x*(s+t);
int dy = my - y*h;
if (my - (x%2)*r < 0) return p; // prevent clicking in the open halfhexes at the top of the screen
//System.out.println("dx=" + dx + " dy=" + dy + " > " + dx*r/t + " <");
//even columns
if (x%2==0) {
if (dy > r) { //bottom half of hexes
if (dx * r /t < dy - r) {
x--;
}
}
if (dy < r) { //top half of hexes
if ((t - dx)*r/t > dy ) {
x--;
y--;
}
}
} else { // odd columns
if (dy > h) { //bottom half of hexes
if (dx * r/t < dy - h) {
x--;
y++;
}
}
if (dy < h) { //top half of hexes
//System.out.println("" + (t- dx)*r/t + " " + (dy - r));
if ((t - dx)*r/t > dy - r) {
x--;
}
}
}
p.x=x;
p.y=y;
return p;
}
In your implementation of paintComponent(), invoke setClip() with a suitable Shape, such as Polygon. You can size and translate the Polygon to match the destination hexagon using the createTransformedShape() method of AffineTransform. Use the coordinates of the polygon's boundary as the basis for the coordinates used in your call to drawImage(). A related example using Ellipse2D is shown here.
I tried to draw a trapezium in Java. That worked out well. Now I'm trying to rotate my trapezium without printing the code in the method rotate(). I was hoping that I could do sth. like call the method rotate() in my testerclass and after calling method draw(), a rotated trapezium would be printed. All in all I'd like to have sth. like
-----*****-----
----*******----
---*********---
--***********--
--***********--
---*********---
----*******----
-----*****-----
My second thought was that I'd like to indent a single trapezium. I'd like to print the trapezium in my other draw(indentation) method.
--***********--
---*********---
----*******----
-----*****-----
Here is my code I figured out so far
public class MyTrapezium {
// background character
private char backgroundChar;
// number of characters at the bottom line of the trapezium.
private int bottomWidth;
// foreground character.
private char foreground;
// number of lines used to draw the trapezium
private int height;
// the size of the margin in chars.
private int margin = 5;
// number of characters at the top line of the trapezium
private int topWidth;
// maximal number of characters per line used to draw the trapezium
private int width = 30;
// Creates a trapezium without a margin with the given width at the top and bottom.
public Trapezium(int topWidth, int bottomWidth) {
this.topWidth = topWidth;
this.bottomWidth = bottomWidth;
}
public MyTrapezium(int topWidth, int bottomWidth, char foreground, char background, int margin) {
this.topWidth = topWidth;
this.bottomWidth = bottomWidth;
this.foreground = foreground;
this.background = background;
this.margin = margin;
}
// Creates a trapezium with the given width at the top and bottom and the size of the margin.
public MyTrapezium(int topWidth, int bottomWidth, int margin) {
this.topWidth = topWidth;
this.bottomWidth = bottomWidth;
this.margin = margin;
}
//Methods
// Draws the trapezium with no indentation.
public void draw() {
int tw = topWidth;
int bw = bottomWidth;
height = bottomWidth - topWidth;
while (tw <= bw) {
for (int i = 0; i < margin; i++) {
System.out.print(background);
}
printChar(background, height / 2);
printChar(foreground, tw);
printChar(background, height / 2);
for (int i = 0; i < margin; i++) {
System.out.print(background);
}
height -= 2;
tw += 2;
}
}
// Rotates the triangle 180 degrees without printing it
public void rotate() {
}
// Draws the triangle with a given indentation.
public void draw(int indentation) {
int i = 0;
while (i <= indentation) {
System.out.print(" ");
i++;
}
}
// Prints a 'run' of a given character.
private void printChar(char character, int length) {
for (int i = 0; i < length; i++) {
System.out.print(character);
}
}
}
public class MyTester {
public static void main(String[] args) {
/** Creates and draws a new trapezium with given values.
* The trapezium is rotated and should be drawn with indentation 15.
*/
Trapezium t = new Trapezium(5, 11, '*', '-', 2);
t.draw();
t.rotate();
t.draw(15);
}
}
As you can see I'm struggling against the two methods rotate() and draw(...). Any advice is welcome. Thank you.
ok, one approach would be to create a buffer and print it then...
a buffer would be a String[] where each String represents a line to be printed...
private String[] buffer;
private void draw(){ //please rename 'draw' to 'print' this is confusiong else
for (String line: buffer){
System.out.println(line);
}
}
public void create(int topWidth, int bottomWidth, char foreground, char background, int margin){
//as done in your code
}
public void rotate(){
String[] newBuffer = new String[buffer.length()];
for(int i = 0; i < buffer.length(); i ++){
newBuffer[buffer.length-1-i] = buffer[i];
}
buffer = newBuffer;
}
public void draw(int indent){
String indentString = createIndent(indent);
for(String line: buffer){
System.out.println(indentString + line);
}
}
private String createIndent(int indent){
String str = "";
for(int i = 0; i < indent; i ++){
str = str + " ";
}
return str;
}
I have a strange problem, I can figure it out.
here is my program running:
the clock on the right shouldn't even be there, it should just be string that displays the time. I create two different objects and add them to a content panel, then add the content panel to the JFrame. I also have a menu bar that I add to the JFrame and it shows up on the right side of the content pane. Here is my main class:
class DigitalClock3DialsView extends DigitalClockView {
private static int caps[] = { BasicStroke.CAP_BUTT,
BasicStroke.CAP_SQUARE, BasicStroke.CAP_ROUND};
private static int joins[] = { BasicStroke.JOIN_MITER,
BasicStroke.JOIN_BEVEL, BasicStroke.JOIN_ROUND};
private static Color colors[] = {Color.gray, Color.pink, Color.lightGray};
private static BasicStroke bs1 = new BasicStroke(1.0f);
// three arms of clock
private Line2D lines[] = new Line2D[3];
private int rAmt[] = new int[lines.length];
private int speed[] = new int[lines.length];
private BasicStroke strokes[] = new BasicStroke[lines.length];
private GeneralPath path;
private Point2D[] pts;
private float size;
private Ellipse2D ellipse = new Ellipse2D.Double();
private BufferedImage bimg;
//variables to keep track if minutes or hours increased
private int oldMinute;
private int oldHour;
/**
* init background
*/
public void init() {
setBackground(Color.white);
}
/**
* reset view: the shape of arms, etc
*/
public void reset(int w, int h)
{
oldMinute = 0;
oldHour = 0;
size = (w > h) ? h/6f : w/6f;
for (int i = 0; i < lines.length; i++) {
lines[i] = new Line2D.Float(0,0,size,0);
strokes[i] = new BasicStroke(size/3, caps[i], joins[i]);
rAmt[i] = 270; // vertical
}
//speed of the 3 arms
speed[0] = 6;
speed[1] = 6;
speed[2] = 6;
path = new GeneralPath();
path.moveTo(size, -size/2);
path.lineTo(size+size/2, 0);
path.lineTo(size, +size/2);
// YW: do not know how to show the regular clock view
// with inc of 5 mins on the contour
// can you help to fix this?
ellipse.setFrame(w/2-size*2-4.5f,h/2-size*2-4.5f,size*4,size*4);
double linApproxLen = 0.75 * size * 0.258819; // sin(15 degree)
PathIterator pi = ellipse.getPathIterator(null, linApproxLen);
Point2D[] points = new Point2D[100];
int num_pts = 0;
while ( !pi.isDone() )
{
float[] pt = new float[6];
switch ( pi.currentSegment(pt) ) {
case FlatteningPathIterator.SEG_MOVETO:
case FlatteningPathIterator.SEG_LINETO:
points[num_pts] = new Point2D.Float(pt[0], pt[1]);
num_pts++;
}
pi.next();
}
pts = new Point2D[num_pts];
System.arraycopy(points, 0, pts, 0, num_pts);
}
private Point2D[] computePoints(double w, double h, int n)
{
Point2D points[] = new Point2D[n];
double angleDeltaRad = Math.PI * 2 / n;
for (int i=0; i<n; i++)
{
double angleRad = i * angleDeltaRad;
double ca = Math.cos(angleRad);
double sa = Math.sin(angleRad);
double x = sa * w/2;
double y = ca * h/2;
points[i] = new Point2D.Double(x,y);
}
return points;
}
public void paint(Graphics g)
{
System.out.printf("seconds: %s, minutes: %d \n", second, minute);
Dimension d = getSize();
updateSecond(d.width, d.height);
if(oldMinute < minute || (oldMinute==59 && minute==0))
{
oldMinute = minute;
updateMinute(d.width, d.height);
}
if(oldHour < hour || (oldHour==12 && hour == 1) )
{
oldHour = hour;
updateHour(d.width, d.height);
}
Graphics2D g2 = createGraphics2D(d.width, d.height);
drawClockArms(d.width, d.height, g2);
g2.dispose();
g.drawImage(bimg, 0, 0, this);
}
public Graphics2D createGraphics2D(int w, int h) {
// standard Java 2D code
Graphics2D g2 = null;
if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) {
bimg = (BufferedImage) createImage(w, h);
reset(w, h);
}
g2 = bimg.createGraphics();
g2.setBackground(getBackground());
g2.clearRect(0, 0, w, h);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
return g2;
}
private void drawClockArms(int w, int h, Graphics2D g2) {
ellipse.setFrame(w/2-size,h/2-size,size*2,size*2);
g2.setColor(Color.black);
g2.draw(ellipse);
for (int i = 0; i < lines.length; i++) {
AffineTransform at = AffineTransform.getTranslateInstance(w/2,h/2);
at.rotate(Math.toRadians(rAmt[i]));
g2.setStroke(strokes[i]);
g2.setColor(colors[i]);
g2.draw(at.createTransformedShape(lines[i]));
g2.draw(at.createTransformedShape(path));
}
g2.setStroke(bs1);
g2.setColor(Color.black);
for (int i = 0; i < pts.length; i++) {
ellipse.setFrame(pts[i].getX(), pts[i].getY(), 9, 9);
g2.draw(ellipse);
}
}
/**
* step forward on the display: move arm forward
*/
public void step(int w, int h) {
for (int i = 0; i < lines.length; i++)
{
rAmt[i] += speed[i];
System.out.println(rAmt[i]);
if (rAmt[i] == 360) {
rAmt[i] = 0;
}
}
}
public void updateSecond(int w, int h) {
rAmt[0] += speed[0];
if (rAmt[0] == 360) {
rAmt[0] = 0;
}
}
public void updateMinute(int w, int h) {
rAmt[1] += speed[1];
if (rAmt[1] == 360) {
rAmt[1] = 0;
}
}
public void updateHour(int w, int h) {
rAmt[2] += speed[2];
if (rAmt[2] == 360) {
rAmt[2] = 0;
}
}
}
and here is the parent class that extends JPanel:
/**
* Digital Clock view base classes
*/
abstract class DigitalClockView extends JPanel
{
protected int second = 0;
protected int minute = 0;
protected int hour = 0;
public void draw()
{
this.repaint();
}
public void updateTime(int second, int minute, int hour)
{
this.second = second;
this.minute = minute;
this.hour = hour;
}
public abstract void paint(Graphics g);
}
my menu buttons are showing on the right side when I use the menu bar. It's almost as if it is just cloning the view on the left side and showing it on the right side again. Why is this?
super.repaint()
Don't do that, as calling repaint from within a paint method has potential for danger. Call the super.paint(g) method inside of a paint(Graphics g) override.
Or even better yet, don't override paint but instead override your JPanel's paintComponent(Graphics g) method and call super.paintComponent(g). For graphics you must call the same super method as the overridden method, and you're not doing that.
Also and again, you should avoid use of null layout as this makes for very inflexible GUI's that while they might look good on one platform look terrible on most other platforms or screen resolutions and that are very difficult to update and maintain.
I'm trying make fonts for a game but whenever I load it I get this error:
Exception in thread "main" java.lang.NullPointerException
at image.ImageFont.load(ImageFont.java:78)
at image.ImageFont.<init>(ImageFont.java:47)
at image.ImageFontTest.init(ImageFontTest.java:26)
at image.GameCore.run(GameCore.java:65)
at image.ImageFontTest.main(ImageFontTest.java:11)
I also have the files for it:
ImageFontTest
public class ImageFontTest extends GameCore {
public static void main(String[] args) {
new ImageFontTest().run();
}
private static final long TOTAL_TIME = 6500;
private ImageFont bigFont;
private ImageFont medFont;
private long remainingTime;
private CharMovement[] charMovement;
public void init() {
super.init();
remainingTime = TOTAL_TIME;
// load image fonts
bigFont = new ImageFont("fonts/big");
medFont = new ImageFont("fonts/medium");
String message = "Good Times!";
int stringWidth = medFont.stringWidth(message);
charMovement = new CharMovement[message.length()];
for (int i=0; i<message.length(); i++) {
charMovement[i] = new CharMovement(message, i,
(screen.getWidth() - stringWidth) / 2,
screen.getHeight() / 2);
}
}
public void update(long elapsedTime) {
remainingTime -= elapsedTime;
if (remainingTime <= 0) {
stop();
}
}
public void draw(Graphics2D g) {
// erase background
g.setColor(Color.BLACK);
g.fillRect(0,0,screen.getWidth(), screen.getHeight());
// draw some aligned text
medFont.drawString(g, "Left", 0, 0,
ImageFont.LEFT | ImageFont.TOP);
medFont.drawString(g, "Center", screen.getWidth()/2, 0,
ImageFont.HCENTER | ImageFont.TOP);
medFont.drawString(g, "Right", screen.getWidth(), 0,
ImageFont.RIGHT | ImageFont.TOP);
// draw seconds remaining
String timeLeft = "" + (remainingTime / 1000);
bigFont.drawString(g, timeLeft, 0, screen.getHeight());
// draw moving characters
double p = (double)(TOTAL_TIME - remainingTime) / TOTAL_TIME;
for (int i=0; i<charMovement.length; i++) {
charMovement[i].draw(g, p);
}
}
/**
Simple animation class to animate a character along a
path.
*/
public class CharMovement {
char ch;
Point[] path;
public CharMovement(String s, int charIndex, int x, int y) {
int stringWidth = medFont.stringWidth(s);
for (int i=0; i<charIndex; i++) {
x+=medFont.charWidth(s.charAt(i));
}
ch = s.charAt(charIndex);
path = new Point[4];
// start offscreen
path[0] = new Point(x-2000, y);
// move to the center of the screen and pause there
path[1] = new Point(x, y);
path[2] = path[1];
// "explode" at a random angle far away
double angle = Math.random() * 2 * Math.PI;
double distance = 1000 + 1000*Math.random();
path[3] = new Point(
(int)Math.round(x + Math.cos(angle) * distance),
(int)Math.round(y + Math.sin(angle) * distance));
}
/**
Draws this character in the path, where p is the
location in the path from 0 to 1.
*/
public void draw(Graphics g, double p) {
int points = path.length - 1;
int index = (int)(p*points);
p = p * points - index;
Point start = path[index % path.length];
Point goal = path[(index + 1) % path.length];
int x = (int)Math.round(goal.x * p + start.x * (1-p));
int y = (int)Math.round(goal.y * p + start.y * (1-p));
medFont.drawChar(g, ch, x, y);
}
}
}
ImageFont
/**
The ImageFont class allows loading and drawing of text
using images for the characters.
Reads all the png images in a directory in the form
"charXX.png" where "XX" is a decimal unicode value.
Characters can have different widths and heights.
*/
public class ImageFont {
public static final int HCENTER = 1;
public static final int VCENTER = 2;
public static final int LEFT = 4;
public static final int RIGHT = 8;
public static final int TOP = 16;
public static final int BOTTOM = 32;
private char firstChar;
private Image[] characters;
private Image invalidCharacter;
/**
Creates a new ImageFont with no characters.
*/
public ImageFont() {
this(null);
firstChar = 0;
characters = new Image[0];
}
/**
Creates a new ImageFont and loads character images from
the specified path.
*/
public ImageFont(String path) {
if (path != null) {
load(path);
}
// make the character used for invalid characters
invalidCharacter =
new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
Graphics g = invalidCharacter.getGraphics();
g.setColor(Color.RED);
g.fillRect(0,0,10,10);
g.dispose();
}
/**
Loads the image files for each character from the
specified path. For example, if "../fonts/large"
is the path, this method searches for all the images
names "charXX.png" in that path, where "XX" is a
decimal unicode value. Not every character image needs
to exist; you can only do numbers or uppercase letters,
for example.
*/
public void load(String path) throws NumberFormatException {
// in this directory:
// load every png file that starts with 'char'
File dir = new File(path);
File[] files = dir.listFiles();
// find min and max chars
char minChar = Character.MAX_VALUE;
char maxChar = Character.MIN_VALUE;
for (int i=0; i<files.length; i++) {
int unicodeValue = getUnicodeValue(files[i]);
if (unicodeValue != -1) {
minChar = (char)Math.min(minChar, unicodeValue);
maxChar = (char)Math.max(maxChar, unicodeValue);
}
}
// load the images
if (minChar < maxChar) {
firstChar = minChar;
characters = new Image[maxChar - minChar + 1];
for (int i=0; i<files.length; i++) {
int unicodeValue = getUnicodeValue(files[i]);
if (unicodeValue != -1) {
int index = unicodeValue - firstChar;
characters[index] = new ImageIcon(
files[i].getAbsolutePath()).getImage();
}
}
}
}
private int getUnicodeValue(File file)
throws NumberFormatException
{
String name = file.getName().toLowerCase();
if (name.startsWith("char") && name.endsWith(".png")) {
String unicodeString =
name.substring(4, name.length() - 4);
return Integer.parseInt(unicodeString);
}
return -1;
}
/**
Gets the image for a specific character. If no image for
the character exists, a special "invalid" character image
is returned.
*/
public Image getImage(char ch) {
int index = ch - firstChar;
if (index < 0 || index >= characters.length ||
characters[index] == null)
{
return invalidCharacter;
}
else {
return characters[index];
}
}
/**
Gets the string width, in pixels, for the specified string.
*/
public int stringWidth(String s) {
int width = 0;
for (int i=0; i<s.length(); i++) {
width += charWidth(s.charAt(i));
}
return width;
}
/**
Gets the char width, in pixels, for the specified char.
*/
public int charWidth(char ch) {
return getImage(ch).getWidth(null);
}
/**
Gets the char height, in pixels, for the specified char.
*/
public int charHeight(char ch) {
return getImage(ch).getHeight(null);
}
/**
Draws the specified string at the (x, y) location.
*/
public void drawString(Graphics g, String s, int x, int y) {
drawString(g, s, x, y, LEFT | BOTTOM);
}
/**
Draws the specified string at the (x, y) location.
*/
public void drawString(Graphics g, String s, int x, int y,
int anchor)
{
if ((anchor & HCENTER) != 0) {
x-=stringWidth(s) / 2;
}
else if ((anchor & RIGHT) != 0) {
x-=stringWidth(s);
}
// clear horizontal flags for char drawing
anchor &= ~HCENTER;
anchor &= ~RIGHT;
// draw the characters
for (int i=0; i<s.length(); i++) {
drawChar(g, s.charAt(i), x, y, anchor);
x+=charWidth(s.charAt(i));
}
}
/**
Draws the specified character at the (x, y) location.
*/
public void drawChar(Graphics g, char ch, int x, int y) {
drawChar(g, ch, x, y, LEFT | BOTTOM);
}
/**
Draws the specified character at the (x, y) location.
*/
public void drawChar(Graphics g, char ch, int x, int y,
int anchor)
{
if ((anchor & HCENTER) != 0) {
x-=charWidth(ch) / 2;
}
else if ((anchor & RIGHT) != 0) {
x-=charWidth(ch);
}
if ((anchor & VCENTER) != 0) {
y-=charHeight(ch) / 2;
}
else if ((anchor & BOTTOM) != 0) {
y-=charHeight(ch);
}
g.drawImage(getImage(ch), x, y, null);
}
}
I see it says the problem is at load in ImageFont but I dont know how to fix it.
Exception in thread "main" java.lang.NullPointerException
at image.ImageFont.load(ImageFont.java:78)
You have a NPE (Null Pointer Exception): this means that you have used a variable or an object which has not been initialized yet.
Example:
Button b;
b.setText("Text in my button"); // NULL POINTER EXCEPTION: b is a button but which button?!?
Solution
Button b = new Button();
b.setText("Text in my button"); // b now is a brand new button and it can be used.
You sir have this kind of problem :)