Hi everybody
for my SimpleGame-project, i've got a global class called Globals (i need that, cause i'm coming from basic / c++)
Now i want to create a Skybox. If i define it all in simpleinitgame it works fine, but
if i do it in "Globals" (no, i didn't forget the import ), i get a NullPointer-Exception.
I rather new to jME and don't know what's going on in the Background. Could anyone help me with that?
I want to keep simpleupdate as lean as possible and therefore i've created several helper-classes - also one for the skybox-update-stuff (dynamic climate system). How can i define a skybox as a "global" variable?
thank & cheers,
Florian
Well some more details about when exactly you initialize your Skybox would be great… For now, I will just assume you create all your globals in the main constructor. What you really need instead is to do it in simpleInitGame. Or maybe you could just have some methods in the globals to allow for custom initialization at a later time (and call these from simpleInitGame).
You can define a global in one place (Globals class) and initialize it in another place (eg simpleInitGame())… just make sure to initialize it before using it. The trick is that most of the stuff in jme should be initialized after you initialize DisplaySystem. The simpleGame you are extending initializes many things for you and then calls simpleInitGame(). So all of your initialization code should be called from simpleInitGame(). That includes skybox.
Having said that I must warn you against using globals. Why?
- Globals lead to many problems because there is no easy way to tell what is using them and when. The more code you have, the more difficult it becomes to tell what state your global variable is in.
2)The whole point behind Object Oriented programming is to move away from globals. You will have problems coupling java classes that are simply not designed to be exposed as globals (you are already having that problem with the skybox). As the old saying goes: "When in Rome, do as the Romans do". So when using Java, follow the principles of Java. It will also be a good learning experience and will arm you with more ideas and tools to get things done.
reading your post makes me think hostile thoughts.
at first, there are no global classes. classes can be public, protected, private or package local. each of these can be either static or an inner class (of another static or inner class).
second, you don't want a global class. if you use it, it will metamorph into a garbage can because you will put everything into it which might be of use at several places.
third, if you have a nullpointer-exception in your code, you should know why. obviously you call a which needs b before b is initialized by c. it seems you're not only new to jme, but to java in general (missing import has nothing to do with a npe)
at last: helper classes are the devils toilet. they are object oriented design upside down.
but to answer your question:
public class Globals
{
private static SkyBox SKYBOX;
public synchronized (<- if multithreaded, use this) SkyBox getSkyBox()
{
if (SKYBOX == null)
{
//generate skybox here
}
return SKYBOX;
}
}
this way you make sure SKYBOX is never null, no matter when you try to access it.
but as i said, this is EVIL. EEEEEVIl
this is called a singleton. it makes sense when many objects need a single instance of something independend from another. in your case, only one object needs the skybox, so it should hold a reference, and nobody else should have access to it.
Let's not be so rude here Hamster… We have just been discussing how it is important to maintain a welcoming environment for newbies too, while managing the growth of the forums/community.
He is asking a mildly valid question (I think) in the sense that he might have code that initializes the Skybox, just a little bit too late for jME to try to start using it. It has happened to me before, and it was a really quick fix. Of course it really depends on what he really is doing with that Global class he has.
Whether you like his coding style or not, you have to admit that the Singleton design pattern is no more than a hack for globals.
Some call it hack, others evil, I prefer the term "Design Pattern" A lazy initialized Singleton is nothing to be ashamed of!
Thanks a lot guys!
…and I got your point: "Globals" in Java are Evil :evil:
@Hamster don't worry, I don't put everything in my Globals.java but some Variables have to be accessed from various classes in various methods in various packages. I don't see another way to do this. You noticed correctly, that I'm also new to Java (1.5 Months). Though I've got some experience (bout 8 years) in high-Level-programming languages. And I try to do better in the future but it's quite a different coding-style than Basic/ASP/WSH/JS. :roll:
This weekend, I'm trying out your suggestions
As a side node, why not simply declare all the variables you need in your class that extends the SimpleGame. Lets say your class is named MySimpleGame.
import com.jme.app.SimpleGame;
import com.jme.scene.Skybox;
public class MySimpleGame extends SimpleGame {
private Skybox skybox;
@Override
protected void simpleInitGame() {
skybox = new Skybox();
// the rest of initialization for skybox
}
protected void simpleUpdate() {
updateSkybox();
}
private void updateSkybox() {
// change the textures on your skybox
}
}
As I said, making globals you are fighting the design concepts behind Java in general and jme in particular. And if you keep on going down that road then most of the work you are doing will be to undo the design patterns put in jme, which technically defeats the purpose of using Java and jme in the first place. Stop fighting it, embrace it ;)
You can learn Object Oriented concepts in under one day, and wont have to scratch your head wondering what is going on anymore.
Here is a great link to learn Java:
http://java.sun.com/docs/books/tutorial/java/TOC.html
I assume you already know alot about programming, so you can probably skip things like operators and language basics, focusing on Objects and Inheritance. If you are going to use Java efficiently, those things are must-know.
It must be one of those things SimpleGame.update() method does for you before and after calling simpleUpdate(). Most likely its the call "rootNode.updateGeometricState(tpf, true);" present in SimpleGame.update(). Technically you could update geometric state on the rootNode from somewhere else, but that method is very expensive and should only be called once per frame (and in a well written, optimized game, maybe even never). So SimpleGame.update() is the best place for it to be called, it's easy to do and works for… well, simple games.
The best solution for you would be to make a member function in your game class. Let's say we your game class is MySimpleGame and you have a function MySimpleGame.updateSkybox(float tpf). Then you can put all of the 100 lines of your skybox update code in that funcion and then call updateSkybox() from your simpleUpdate(). That way you keep all that code away from simpleUpdate() and the same time calling it at the right moment.
As a rule of thumb, anything you update outside of simpleUpdate() may give strange result if you don't know what is going on behind the scenes and take appropriate steps to ensure everything that needs to be called is getting called.
cool! it worked (so far). the solution was to initialise it after displaysystem. thanks a lot.
unfortunately, another thing regarding skyboxes gives me headaches now (i tried for hours before posting this).
my final goal is to have changing textures (10 changes per day -> 60 textures) on the skybox. behind this changes is some code (about 100 lines) so i definitely want to keep this out of simpleupdategame().
problem: if i update the textures outside simpleupdategame() with
mySkyBox.setTexture(Skybox.NORTH,TextureManager.loadTexture.....)
thanks for your valuable information. i'll have a look at suns page this weekend to get more information about oop (than written in my rather bad java-book).
unfortunately, i still wasn't able to achieve my goal, even according to your code-structure. Did your code-structure work on your PC? I'm asking that, because i wanna make sure, there's nothing else in my code which could influence the skybox.
I copied your code from above and inserted comments. any more ideas? I'm at a loss here. it definitely should work. i even analyzed Skybox.java and couldn't find a reason there
please post the not working code. if possible, in a compilable state using textures delivered with jme so we can c&p & run it.
overasked gibt es
@Hamster: thx for the language-tipp. currently, I’m investing my somewhat limited time to check out skydome and adapting it to use my own ingame-time-model. so far i’m stunned. http://www.cs.utah.edu/vissim/papers/sunsky/ since the results of the (unmodified) sample-code are allready great, I don’t think I wanna spend more time using skyboxes. in the future, i might also be able to adapt the skydome-class do render procedurally clouds.
if you like, i can post the code anyway, but i’d have to strip out everything unneccessary for readability. since that would take me half an hour (but i’d gladly do it, if your interested) I’d rather confirm wheter you wanna analyse it.
have a nice weekend
if there is no longer a problem, the answer is:
obvious
Well, if you decide to keep it simple and use skybox (or someone else is looking at this post to get their skybox working), here is a runnable example that will change skybox textures every 5 seconds:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme.app.SimpleGame;
import com.jme.image.Image;
import com.jme.image.Texture;
import com.jme.scene.Skybox;
import com.jme.util.TextureKey;
import com.jme.util.TextureManager;
public class MySimpleGame extends SimpleGame {
private Skybox skybox;
private float updateTimer = 0;
private float updateInterval = 5;
private Texture[][] textures;
private int textureIndex;
@Override
protected void simpleInitGame() {
setupSkybox();
}
private void setupSkybox() {
skybox = new Skybox("Sky", 10, 10, 10);
textures = new Texture[2][];
textures[0] = new Texture[]{
loadTexture(sky1Top, ".png"),
loadTexture(sky1Sides, ".png"),
loadTexture(skyAllBottom, ".png")};
textures[1] = new Texture[]{
loadTexture(sky2Top, ".png"),
loadTexture(sky2Sides, ".png"),
loadTexture(skyAllBottom, ".png")};
textureIndex = 1;
setSkyboxTextures();
rootNode.attachChild(skybox);
}
private void updateSkybox() {
skybox.setLocalTranslation(cam.getLocation());
updateTimer += tpf;
if (updateTimer > updateInterval) {
updateTimer = 0;
textureIndex++;
if (textureIndex >= textures.length) textureIndex = 0;
setSkyboxTextures();
}
}
private void setSkyboxTextures() {
Texture top = textures[textureIndex][0];
Texture sides = textures[textureIndex][1];
Texture bottom = textures[textureIndex][2];
skybox.setTexture(Skybox.NORTH, sides);
skybox.setTexture(Skybox.EAST, sides);
skybox.setTexture(Skybox.SOUTH, sides);
skybox.setTexture(Skybox.WEST, sides);
skybox.setTexture(Skybox.UP, top);
skybox.setTexture(Skybox.DOWN, bottom);
skybox.preloadTextures();
skybox.updateRenderState();
}
protected void simpleUpdate() {
updateSkybox();
}
public static void main(String[] args) {
Logger.getLogger("").setLevel(Level.WARNING);
MySimpleGame game = new MySimpleGame();
game.setDialogBehaviour(MySimpleGame.ALWAYS_SHOW_PROPS_DIALOG);
game.start();
}
// from here on is just texture loading...
private static Texture loadTexture(String encoded, String ext) {
try {
Image image = TextureManager.loadImage(ext,
new ByteArrayInputStream(decode(encoded)), true);
TextureKey tkey = new TextureKey(
new URL("file", "", -1, String.valueOf(encoded.hashCode())),
Texture.MM_LINEAR,
Texture.FM_LINEAR,
1.0f,
true,
image.getType());
return TextureManager.loadTexture(null, tkey, image);
} catch (IOException ioe) {
return null;
}
}
private static char skipChar = '=';
private static String code = "ABCDEFGHIJKLMNOPQRSTU" +
"VWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
private static byte[] decode(String data) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int index = 0;
int charsIn = 0;
int bits = 0;
int keep = -1;
while (index < data.length()) {
if (charsIn < 4) {
int decodedChar = code.indexOf(data.charAt(index));
if (decodedChar != -1) {
bits = bits << 6;
bits = bits | decodedChar;
keep++;
charsIn++;
} else if (data.charAt(index) == skipChar) {
bits = bits << 6;
charsIn++;
}
index++;
}
if (charsIn == 4) {
charsIn = 0;
for (int k = 2; k >= 0; k--) {
int decodedByte = (bits >> 8*k) & 0xFF;
if (keep > 0) {
bos.write(decodedByte);
keep--;
}
}
bits = 0;
keep = -1;
}
}
return bos.toByteArray();
}
private static String skyAllBottom =
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAGklEQVQoz2M0tX3OQApgYiAR" +
"jGoY1TB0NAAAUXgBeXQRbNUAAAAASUVORK5CYII=";
private static String sky1Top =
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAGklEQVQoz2O8mSfJQApgYiAR" +
"jGoY1TB0NAAAaEwBgOcWewoAAAAASUVORK5CYII=";
private static String sky1Sides =
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAA4klEQVQoz3WSsXLDMAxDgZ7G" +
"xp3zk/nefkh118HO60CRkpKrJpkmQACUvx93zeO8oJeDZUm03+szCkiWkTzb4+oV2Pp1K26Q" +
"s4cA2VpBqPXz2H4gGfiQsUQoYYgSav065Gg1ki0YfCUsRgrjAEjIUYpeSgTla0gegBlGYqZy" +
"yqPTNJ5cocFv2dpCEq2fX8wgnd62KEkreJH0tqptD6TG1s+RUnwHvQF7GmEytv68SYaFsaQZ" +
"Ud4dhdbPg3+kZ9ybmdqDZkjbY6i8HMtpP9cxuFknjTUmk1Ngmo5F8PI2Nw9j3h8YlpGXMwdR" +
"RQAAAABJRU5ErkJggg==";
private static String sky2Top =
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAEElEQVQoz2NgGAWjYBTAAAAD" +
"EAABaJFtwwAAAABJRU5ErkJggg==";
private static String sky2Sides =
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAA4UlEQVQoz5WSQZLEIAwDW4Xm" +
"uEX+/9hoD8bAHCcHYhKBTAv48dEYfygkoAQIioAQao5WEQUPTwiC1BY5Y4ii9AREPD6TtFwp" +
"FyBVHK8SxR5PVVKL6N1ptSKohm0/5VkLSlR9Z5ek12N7rjOu3/USvNXRWQxKPD7P3md9V5Ij" +
"qq4KoYI9JiLbQSFit3w5C6J2aOLLmKQgsBNoSw/PncvCfcivg0I2ENsPhG91tlvhLwosrPP+" +
"vQhdeSzonZ+HH5Qr26guzd3aNdqfuSE2ypOG8gadHIiHJ9kRFNF3X9KvuwKQf7qdxg/VLoDu" +
"AAAAAElFTkSuQmCC";
}
thanks However, I'm allready almost finished coding a rather advanced skydome. I'm just about to post a question to that in "graphic related problems".