I'm currently making my first tile-based game in JavaFX, and quite early on I've run into an issue. I want to make the size of the object in the game (including the map) resize with the window, so that you won't be able to see a larger part of the game if you resize your window.
I'm using a Scale class which calculates the size of tiles. To make it responsible to rescaling of the window, it uses properties. The viewport variable is a rectangle with the width and height of the window:
public class Scale {
private static DoubleProperty widthProperty, heightProperty;
private static DoubleBinding tileWidthProperty, tileHeightProperty;
public static void initialize(Rectangle viewport){
widthProperty = viewport.widthProperty();
heightProperty = viewport.heightProperty();
tileWidthProperty = tileHeightProperty = new DoubleBinding() {
{
super.bind(widthProperty, heightProperty); // initial bind
}
#Override
protected double computeValue() {
return Math.round(Math.max(widthProperty.get(), heightProperty.get())/32);
}
};
}
public static DoubleBinding x(int n){
return tileWidthProperty.multiply(n);
}
public static DoubleBinding y(int n){
return tileHeightProperty.multiply(n);
}
}
All objects base their size and position on these tileWidthProperty and tileHeightProperty variables, like so:
Tree tree = new Tree();
tree.widthProperty.bind(Scale.x(2));
tree.heightProperty.bind(Scale.y(3));
tree.xProperty.bind(Scale.x(12));
tree.yProperty.bind(Scale.x(2));
All tiles are scaled and positioned in the same way.
When first starting the game, there is little to no lag. However, when the window is resized and all the properties change, the client freezes. My question is: Is there any way to optimize this resizing? Should I do it in a different way than I am with these properties? If so, what would be most efficient?
Related
Aka Can I have a 3d scene that is in front of the main scene without it being clipped.
Aka The front of my weapon keeps getting hidden by the scene in my game
Situation
I have a 3d scene, rendered by JMonkey, and additionally have 3d weapons held by the player. Additionally the player may stand so close to walls that they may (if treated realistically) stick the end of their gun through the wall. This means that if the gun is placed "by dead reckoning" in the scene so it looks right the end of the gun gets clipped off and looks stupid.
Because this is a 3d object (that may be rotated or moved) I can't just use a 2d image and put it in the gui layer
Create an additional camera and view port to support these additional graphics
In JMonkeyEngine its possible to have more than one viewport and layer them on top of each other to provide the described effect. Each viewport is internally 3d, but the view ports themselves layer over the top of each other.
Code example
Main class to get things set up
public class Example extends SimpleApplication {
public static void main(String[] args){
Example app = new Example();
app.start(); // start the game
}
#Override
public void simpleInitApp() {
//this represents the "real world" in this example
Box b = new Box(1, 1, 1);
Geometry geom = new Geometry("Box", b);
Material mat = new Material(assetManager,"Common/MatDefs/Misc/Unshaded.j3md");
mat.setColor("Color", ColorRGBA.Blue);
geom.setMaterial(mat);
rootNode.attachChild(geom);
this.stateManager.attach(new UnderGui());
}
}
An UnderGui layer, that has a 3d scene between the gui layer and the main 3d scene
/**
* The under gui is for things like the held tool. Things that are kind of 3d but also want to be "over" everything
* else
*/
public class UnderGui extends AbstractAppState {
private Node root;
public UnderGui() {
}
#Override
public void initialize(AppStateManager stateManager, Application app )
{
root = new Node( "Under gui viewport Root" );
Camera originalCam = app.getCamera();
Camera cam = new Camera(originalCam.getWidth(), originalCam.getHeight());
//the default camera is an orthoganal camera, use the main cam to copy sensible camera properties
cam.setParallelProjection(originalCam.isParallelProjection());
cam.setFrustum(originalCam.getFrustumNear(), originalCam.getFrustumFar(), originalCam.getFrustumLeft(), originalCam.getFrustumRight(),originalCam.getFrustumTop(), originalCam.getFrustumBottom() );
cam.setFov(originalCam.getFov());
//camera is looking in a +Z direction. -X is right, +X is left. +Y is up
ViewPort view = app.getRenderManager().createMainView("Under gui ViewPort", cam);
view.setEnabled(true);
view.setClearFlags(false, true, false);
view.attachScene( root );
root.attachChild(createWeapon(app.getAssetManager()));
}
/**
* This just creates a red box to be a "weapon" as an example
* #return
*/
private Geometry createWeapon(AssetManager assetManager){
Box b = new Box(1, 1, 1); // create cube shape
Geometry geom = new Geometry("Box", b); // create cube geometry from the shape
Material mat = new Material(assetManager,
"Common/MatDefs/Misc/Unshaded.j3md"); // create a simple material
mat.setColor("Color", ColorRGBA.Blue); // set color of material to blue
geom.setMaterial(mat); // set the cube's material
geom.setLocalTranslation(-2,0,2);
return geom;
}
#Override
public void render(RenderManager rm) {
root.updateGeometricState();
}
#Override
public void update( float tpf ) {
root.updateLogicalState(tpf);
}
}
This can also be used for any "ability to see through walls" overlay
Alternative bad solution; setDepthTest
You can also just put the weapon in the main scene and set geometry.getMaterial().getAdditionalRenderState().setDepthTest(false);. However this is a less good option because it requires the weapon to constantly be moved as the camera changes position and direction and can only cope with "simple" 3D overlays as if 1 part of the weapon is in front of another part (e.g. the stock is in front of the trigger) which bit is rendered is random, as you've asked JME not to do any depth testing
Very new here. Trying to learn Java.
I figured I'd write a very simple "Game Of Life" auto-algorithm to see if I could grasp some of the basics - (The syntax and concepts are pretty straightforward from other languages etc.)
I've managed to muddle through and seem to be "getting the hang of it" but I've reached a pretty steep hill now.
The code described here will compile and is executable, but as you will see, despite the #override to DrawComponent and the repaint() call, the actual image displayed on-screen does not appear to change...
DIRECT QUESTION IS:
How can I ensure the display is updated every "loop"?
FLUFF & SAMPLE SOURCE:
I appreciate the procedural and non-distributed // rigid single encapsulated class here is not the ideal approach for Java, nor is this "game of life" a particularly appropriate use fothe language - BUT Iwanted to focus on getting core elements and understanding first before I 'advance' onto other things. Secondly, I appreciate this is not a very efficient solution either, but again, I wanted to learn by trying something straightforward and readable so I could easily SEE what was happening and ensure it was working properly as expected.
Third, my naming or formatting conventions may be unusual or non-standard, for this I apologise and again appreciate there are preferred ways. This was not necessarily a priority for me in learning how the language works at this time.
I welcome any advice and tips but please consider I am a complete newcomer to using Java so may not be familiar with concepts and terminologies etc.
/*
Project.java
By: PJ Chowdhury
Entire program encapsulated in single class
Created 29-Oct-2018
Updated: 07-Nov-2018
Added graphics library
*/
//Import the basic required graphics classes.
import java.awt.image.BufferedImage;
import java.awt.*;
//Import the basic applet classes.
import javax.swing.*;
//Core class
public class project
{
//Control value - determines when overpopualted or extinct
private static int supercount;
//Control value - how many surrounding cells must be alive for the central cell to survive
private static byte thrive=4;
//Define & declare effective constant size values
private static byte size=64;
private static byte cellsize=4;
//Declare effective singleton arrays of cells
private static boolean[][] cells;
private static boolean[][] prolif;
//Declare Window Frame
public static JFrame frame;
//Declare Graphics
public static JPanel panel;
//main entry-point. Execution must include parameter argument.
public static void main(String[] args)
{
initialise();
do
{
runtime();
defaultcells();
}
while (1>0); //Bad form of course. I wanted an infinite loop. The window can be closed at user request.
}
//Initialises window & graphics frame
public static void initialiseframe()
{
//Create Window
frame = new JFrame("Life Cells");
//Define window parameters
frame.setSize((int)cellsize*size,(int)cellsize*size);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
//Create a window panel to accept graphics
panel = new JPanel()
{
//Overload PaintComponent method to redraw image when the frame panel is redrawn
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
}
};
//attach this panel as a gadget to the frame window
frame.add(panel);
//frame.pack();// Deprecated as this resizes frame window to a minimal size
frame.validate(); // required since panel was added after setVisible was called
frame.repaint(); // required since panel was added after setVisible was called
}
//Initialises & defaults cells
public static void initialisecells()
{
//Define array sizes
cells = new boolean[size][size];
prolif = new boolean[size][size];
// Populate with defaults
defaultcells();
}
//Sets randomised state for each cell
public static void defaultcells()
{
byte x;
byte y;
for (y=0;y<size;y++)
{
for (x=0;x<size;x++)
{
if (Math.random()>=0.5)
{
}
else
{
}
}
}
}
//Wraps initialisation routines
public static void initialise()
{
initialiseframe();
initialisecells();
}
//iterates cells (twice) to determine if they survive or decline and draw to image
public static void process()
{
//Prepare image for cell drawing
Graphics g=panel.getGraphics();
byte x;
byte y;
supercount=0;
//First pass - check if cell will thrive
for (y=0;y<size;y++)
{
for (x=0;x<size;x++)
{
checkcell(x,y);
}
}
//Second pass - apply thrive or wither
for (y=0;y<size;y++)
{
for (x=0;x<size;x++)
{
if (updatecell(x,y))
{
}
if (cells[x][y])
{
}
}
}
}
//sets prolif equivalent depending on status of surrounding cells. This is used in update to set these cells to thrive
public static void checkcell(byte x, byte y)
{
byte count=getsurrounding((int)x,(int)y);
if (count>thrive)
{
prolif[x][y]=true;
}
else
{
if (count<thrive)
{
prolif[x][y]=false;
}
else
{
prolif[x][y]=cells[x][y];
}
}
}
//updates cell with prolif equivalent and returns true if cahnged
public static boolean updatecell(byte x, byte y)
{
if (cells[x][y]!=prolif[x][y])
{
cells[x][y]=prolif[x][y];
return true;
}
return false;
}
//returns number of thriving "cells" surrounding cell at given coords
public static byte getsurrounding(int x, int y)
{
int u=(x-1);
int v=(y-1);
int ux;
int vy;
byte count=0;
for (v=(y-1);v<(y+2);v++)
{
//Use wraparound for edge cells
vy=(v+size) % size;
for (u=(x-1);u<(x+2);u++)
{
//Use wraparound for edge cells
ux=(u+size) % size;
//Only for surrounding cells, not this cell
if ((ux!=x) & (vy!=y))
{
}
}
}
return count;
}
//Draw cell at x,y : not the most efficient nor elegant method...
public static void drawcell(Graphics g, int x, int y, boolean live)
{
if (live)
{
// Draw this cell alive
//g.setColor(Color.GREEN);
}
else
{
// Draw this cell dead
g.setColor(Color.BLACK);
}
g.fillRect(x*cellsize, y*cellsize,cellsize,cellsize);
panel.repaint(x*cellsize, y*cellsize,cellsize,cellsize);
}
//Returns true if population is healthy. False if extinct or overcrowded
public static boolean populationanalysis()
{
return ((supercount<thrive)||(supercount>(int)(size*size)<<1));
}
//Main Loop method
public static void runtime()
{
int sanity=5000;
int loopcount=0;
do
{
process();
loopcount++;
if (populationanalysis())
{
break;
}
}
while (loopcount<sanity);
}
}
For organization's sake, I use multiple scenes for my game and rather than having each scene have a constructor that receives a Viewport (my game is scalable), I would like to set each stage's viewport separate of the constructor, then after the viewport is set, add the actors. In the main class, it would happen like this:
public void setStage(Stage s)
{
if(currentStage != null)
currentStage.dispose();
currentStage = s;
currentStage.setViewport(view);
}
To make this go fluidly, each stage has an init method that is called within an overriden setViewport:
#Override
public void setViewport(Viewport v)
{
super.setViewport(v);
init();
}
However, all this gives me is a black screen... I have tried updating the camera and viewport, but no avail (note that the actors are having their render methods called).
Why am I getting this black screen and how do I fix it? If it's not possible I'll just revert to using the constructor.
If I understood correctly you want to do this:
Stage stage1 = new Stage();
stage1.getViewport().update(width, height);
rather than this:
Stage stage1 = new Stage (new StretchViewport(width, height)); // It doesn't have to be StretchViewport
In the first case (what you are trying to do) a ScalingViewport will be costructed automatically for you with dimensions of the Gdx.graphics and an orthographic camera and acts like a StretchViewport. Why not using the second case directly where you pass the viewport you want. You can always alter your viewport whenever you want by calling stage1.getViewport().update(width, height);
or by calling stage1.setViewport(width, height, false); in older Libgdx versions.
Viewport has changed recently so if you can extend Viewport class to Override the update method maybe you can achieve what you want:
public class ViewportExtendClass extends StretchViewport{
public ViewportExtendClass(float worldWidth, float worldHeight) {
super(worldWidth, worldHeight);
}
#Override
public void update (int screenWidth, int screenHeight, boolean centerCamera) {
super.update(screenWidth, screenHeight, centerCamera);
// DO YOUR INITIALIZATION HERE
}
}
From your main class you create new stage :
Stage stage1 = new Stage (new ViewportExtendClass (width, height));
and then you call :
stage1.getViewport().update(width, height);
Like this you can alter stage viewport and re initialize your assets.
#Override
public void setViewport(Viewport v)
{
super.setViewport(v);
this.getViewport().update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), false);
Camera c = this.getViewport().getCamera();
c.position.set(c.viewportWidth/2, c.viewportHeight/2, 0);
init();
}
This works, but you should also be able to update the Viewport like that at the begin of your application, if you continue to use the same one. I set the position like that instead of centering because some of my Stages will be larger than the screen.
I am making a little tick tac toe game to get the hang of gui in java.
It was going very well till I ran into the problem I mentioned in the title. My Jbuttons have a transparent background. However, when my mouse entered the button, the button's background became little pieces of the background that last had focus. For Example, if my frame had the focus it would take a little bit of that and set it as the background.
By adding this.setRolloverEnabled(false) to the code it no longer had this weird bug when I hovered over it, but the bug still occurs when I click the button.
When the button is clicked it changes it icon to either an x or an o image but the problem still occurs
Here is my code for my buttons
public class Tile extends JButton implements ActionListener
{
private Board myBoard;
private int ID;
private int state; //0 = blank, 1 = x, 2 = O
private Icon x;
private Icon o;
private Icon blank;
public Tile()
{
blank = new ImageIcon(getClass().getResource("/Resources/Tiles/blank.png"));
x = new ImageIcon(getClass().getResource("/Resources/Tiles/x.png"));
o = new ImageIcon(getClass().getResource("/Resources/Tiles/o.png"));
state = 0;
setIcon(blank);
addActionListener(this);
this.setBorder(new BorderUIResource.EmptyBorderUIResource(0,0,0,0));
this.setBorderPainted(false);
this.setContentAreaFilled(false);
this.setFocusPainted(false);
this.setRolloverEnabled(false);
}
public Tile(int ID)
{
this();
this.ID = ID;
}
public Tile(int ID, Board myBoard)
{
this(ID);
this.myBoard = myBoard;
}
public void updateState(EnumTileState newState)
{
switch(newState)
{
case BLANK : state = 0;
break;
case X_STATE : state = 1;
break;
case O_STATE : state = 2;
break;
default: return;
}
update();
}
private void update()
{
switch(state)
{
case 1: this.setIcon(x);
break;
case 2: setIcon(o);
break;
default: setIcon(blank);
break;
}
}
public int getID()
{
return ID;
}
public int getState()
{
return state;
}
public void actionPerformed(ActionEvent event)
{
Tile clickedTile = (Tile) event.getSource();
if(myBoard != null)
{
myBoard.clickReceived(clickedTile.getID());
}
}
}
I would attach images but I don't have a high enough reputation so here is a link to an Imgur album
Hope I didn't make any silly mistakes when posting this. Its my first question. Anyway thanks in advance for any help you are able to provide
This has to how the paint engine deals of opaque components. Basically, when dealing with opaque components, the paint engine doesn't prepare the background the component (that is, it won't paint what is actually behind the component). Instead, it leaves it up to the component at hand to "clear" the Graphics context as is needed.
Try making the button transparent by using setOpaque(false)
Updated
The major problem is you're supply alpha based colors (new Color(0, 0, 0, 0)) to opaque components. This means that the paint engine does not consider the components it is painting to be transparent, so it makes no special effort to prepare the Graphics context, but when the component tries to fill it's content (with the background color), it is filling it with a transparent color, which is pretty much the same as not calling super.paintComponent.
Make ALL your JPanel based components transparent except the base panel.
I'm developing a Tower Defense game using libGDX. I've just started and I am able to display the path, the "environment" and the enemies walking along the path.
I displayed the environment using a SpriteBatch-Object, like this:
public LevelController(Level level) {
spriteBach = new SpriteBach();
atlas = new TextureAtlas(Gdx.files.internal("images/textures/textures.pack"));
}
public void setSize() {
spriteBatch.setProjectionMatrix(this.cam.combined);
}
public void render() {
spriteBatch.begin();
drawTowerBases();
spriteBatch.end();
}
private void drawTowerBases() {
// for each tower base (=environment square)
TextureRegion towerBaseTexture = atlas.findRegion("TowerBase");
if(towerBaseTexture != null) {
spriteBatch.draw(towerBaseTexture, x, y, 1f, 1f);
}
}
This is working properly and the textures are displayed well: Tower Defense using spriteBatch
Now, I was wondering if it is possible to cache the background. Because it stays the same, there is no need to calculate it every time. I've found the SpriteCache through Google-Search. So I changed the code as follows:
public LevelController(Level Level) {
spriteCache= new SpriteCache();
atlas = new TextureAtlas(Gdx.files.internal("images/textures/textures.pack"));
spriteCache.beginCache();
this.drawTowerBases();
this.spriteCacheEnvironmentId = spriteCache.endCache();
}
public void setSize() {
this.cam.update();
spriteCache.setProjectionMatrix(this.cam.combined);
}
public void render() {
spriteCache.begin();
spriteCache.draw(this.spriteCacheEnvironmentId);
spriteCache.end();
}
private void drawTowerBases() {
// for each tower base (=environment square)
TextureRegion towerBaseTexture = atlas.findRegion("TowerBase");
if(towerBaseTexture != null) {
spriteCache.add(towerBaseTexture, x, y, 1f, 1f);
}
}
And now the game looks like this: Tower Defense using spriteCache
For me it seems as if the transparency is not rendered properly. If I take images without transparency, everything works fine. Does anyone have an idea, why this happens and how I can fix this?
Thank you in advance.
Taken from the SpriteCache documentation:
Note that SpriteCache does not manage blending. You will need to enable blending (Gdx.gl.glEnable(GL10.GL_BLEND);) and set the blend func as needed before or between calls to draw(int).
I guess there is not much more to say.