I just started creating games for Android and decided to begin by following Retro Chicken's tutorial.
What I just don't get is where the game actually starts to listen for commands?
Usually they'd have a function like public void run () that is called.
The main is like :
game = new GamePanel(this);
setContentView(game);
And the constructor called of the main is :
public GamePanel(Context context){
super(context);
this.getHolder().addCallback(this);
thread = new MainThread(getHolder(), this); // Inherits [Thread] class. Code posted
player = new RectPlayer(new Rect(100,100,200,200), Color.rgb(255,0,0)); Class made by me. Has nothing special.
playerPoint = new Point(150,150);
// We ensure that THIS canvas will get the focus.
setFocusable(true);
}
I surfed through some super calls, but still nothing
MainThread.java
GamePanel.java
EDIT: I am interested in what calls the run function, where and how ?
This is the block that actually starts the controller thread,
#Override
public void surfaceCreated(SurfaceHolder holder){
thread = new MainThread(getHolder(), this);
thread.setRunning(true);
thread.start();
}
Since class MainThread extends Thread, calling the start method of the Thread object will call the run method on a new Thread.
Now how does this method run?
#Override
public void surfaceCreated
This is an override of the implements SurfaceHolder.Callback definition.
Your game object knows about the interface, because the constructor passes a reference of itself to the holder, itself, containing the interface to be called.
this.getHolder().addCallback(this);
The holder now has a reference to this class, and can call
callback.surfaceCreated();
Where surfaceCreated is interface method you implemented.
Now how did the surface get created?
The surfaceCreated method is called when:
This is called immediately after the surface is first created. Implementations of this should start up whatever rendering code they desire. Note that only one thread can ever draw into a Surface, so you should not draw into the Surface here if your normal rendering will be in another thread.
You started that process by calling finally calling setContentView(game); which inflated the surface.
GamePanel class is SurfaceView, that provide drawing surface. You are adding a layer by setContentView(game);.
Now you need to start drawing. In drawing you need to erase and draw again if you need to change something on Paint Board(Surface View).
Now you need to call draw (Canvas canvas) again and again for the previous purpose. So you're using MainThread.
Look at your run() method of your MainThread, there is a infinite while(true) loop. By that loop your your GamePanel's draw (Canvas canvas) runs call at particular time duration.
Here is where the main thread gets startet, if its that what you want to know.
#Override
public void surfaceCreated(SurfaceHolder holder){
thread = new MainThread(getHolder(), this);
thread.setRunning(true);
thread.start();
}
I think you should specify your question a little bit.
Here is the callback method, that handles touches on the gamepanel.
#Override
public boolean onTouchEvent(MotionEvent event){
/* [event.getAction()] : returneaza un [int] care are o valoeare
in functie de ce fel de touch e
*/
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
playerPoint.set((int)event.getX(), (int)event.getY());
}
return true;
}
Related
I am currently developing a tower defence game for a university project.
At higher waves there are hundreds of enemies moving arround. My problem is that it's getting very inperforment at about 300+ enemies moving simultaneously.
Every enemy is a children of a Pane which is displayed in my scene.
I've got a method in my EnemyGraphics class which updates the positon by calling the update method:
public class EnemyGraphics extends ImageView implements EventObserver {
...
#Override
public void update() {
Platform.runLater(() -> {
relocate(enemy.getxCoordinate(), enemy.getyCoordinate());
});
}
}
I guess it's getting laggy because every enemy is updating its location on its own every time it moves by calling update().
Is there a way that I can set new coordinates for my ImageView object, without redrawing the scene and in the main FX-Thread creating a timer which redraws the entire scene in a certain interval? Or is there a other solution / method I can call to move images performantly over the pane?
Ok I've found my mistake. Everytime my enemyLogic class compute a new position it called the enemyGraphic object to update its position. During testing I've removed the functionality of the method in the enemyGraphics class but not the call.
To update the postions of all the enemys I've written a method in my GUIcontroller class:
private void startUpdateTicker() {
final AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long timestamp) {
updateEnemies();
}
};
timer.start();
}
public synchronized void updateEnemies() {
for (EnemieGUI enemy : enemyList) {
enemy.relocate(enemy.getEnemie().getxCoordinate(), enemy.getEnemie().getyCoordinate());
}
}
I have a problem, I think its related to Screen render and its lifecycle.
Basically I have two screens (Menu and Game). In GameScreen render method i call World.update and after that my Render. In hide method (of GameScreen) i dispose of the SpriteBatch from Redner class.
So when I change the screen from Game to Menu (within World.update) Java crashes. As far as I can tell, the dispose is making the crash.
So my question is, when i set a new screen in the middle of the render cycle, is that render cycle still going to finish with its old screen? Meaning, am I calling batch.dispose before the rendering was finished, and that is why i get the problem?
Thank you for all the help
public class GameScreen extends AbstractGameScreen {
private static final String TAG = GameScreen.class.getName();
private WorldController worldController;
private WorldRenderer worldRenderer;
private boolean paused;
public GameScreen(Game game) {
super(game);
}
#Override
public void render(float deltaTime) {
// Do not update game world when paused
if (!paused) {
// Update game world by the time that has passed since last render time
worldController.update(deltaTime);
}
// Sets the clear screen color to: Cornflower Blue
Gdx.gl.glClearColor(0x64 / 255.0f, 0x95 / 255.0f, 0xed / 255.0f, 0xff / 255.0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// Render game world to screen
worldRenderer.render();
}
#Override
public void resize(int width, int height) {
worldRenderer.resize(width, height);
}
#Override
public void show() { // Similar as create method
worldController = new WorldController(game);
worldRenderer = new WorldRenderer(worldController);
Gdx.input.setCatchBackKey(true);
}
#Override
public void hide() { // Similar to dispose method
worldRenderer.dispose();
Gdx.input.setCatchBackKey(false);
}
#Override
public void pause() {
paused = true;
}
#Override
public void resume() {
super.resume();
// Only called on Android
paused = false;
}
}
That's basically correct. The screen that calls setScreen from within its render method will have hide called on itself and then will continue through the rest of its code in its render method. So you are killing your sprite batch right before trying to draw with it.
So don't call dispose from within your hide method. In fact, it is probably bad practice for a Screen to ever call dispose on itself. You can reserve that for the Game class that owns it. For example, you could do something like this in your game class:
#Override
public void render() {
super.render();
if (getScreen() != gameScreen && gameScreen != null) {
gameScreen.dispose();
gameScreen = null;
}
}
By the way, you should probably put the SpriteBatch in your Game subclass and let all the different screens share it. It's a fairly big object to be allocating and deallocating for no reason.
Problem:
Main.repaint() doesn't work for me. repaint() doesnt invoke my paint method in Main. I've tried calling validate before repainting but with no succes. Main paints perfectly initially or when resized but when i call repaint() in my code nothing is happening.
Here is how the program looks so far link
So im trying to create a level selection screen for a game in java. My game is a JApplet. I have a structure as follows:
my Main class which extends JApplet and contains an object of
LevelScreen class
LevelScreen has a paint method which Main invokes.
I tried to avoid using Swing since the layout managers gave me trouble with the design. So I've tried to make a structure which were simpler and more suited for my need.
paint() in Main.java
public class Main extends JApplet {
public static final int WIDTH = 700, HEIGHT = 500;
private static Main instance;
private LevelScreen levelScreen = new LevelScreen();
private View view = View.LEVELSCREEN;
public static Main getInstance() {
if (instance == null)
instance = new Main();
return instance;
}
#Override
public void init() {
setSize(WIDTH, HEIGHT);
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
Point p = e.getPoint();
if (view == View.LEVELSCREEN) {
levelScreen.mouseMoved(p);
}
}
});
}
#Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if (view == View.LEVELSCREEN)
levelScreen.paint(g2);
}
public enum View {
GAME, LEVELSCREEN;
}
}
In the code of my Buttons i try to repaint Main because i want to make a fade out animation when mouse leaves the button. my problem is that i cant invoke the paint(Graphics g) in main with repaint()
Here i call repaint():
public void mouseExited() {
//start new thread to make fade out animation when mouse leave
mouseOver = false;
TimerTask task = new TimerTask() {
#Override
public void run() {
while (!mouseOver && opacity > 0.6) {
opacity -= 0.02;
//set level to 999 so i can see if the game repaints()
level = 999;
Main.getInstance().repaint(); //this doesnt work!!
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
new Thread(task).start();
}
This is a problem with the way that you implement the singleton design pattern. The way you do it doesn't work for an applet, where the instance is created for you by the applet container. You can fix it by changing getInstance as follows:
public Main getInstance() {
return instance;
}
And add this line to the init method:
instance = this;
By the way, you should not override paint in a Swing component, which a JApplet is. You should override paintComponent instead, and call super.paintComponent(g) as the first line. This should fix the problem.
Main.getInstance().repaint(); //this doesnt work!!
I'm not surprised. You're not the one creating the instance of the JApplet, the browser is.
When you call this...
public static Main getInstance() {
if (instance == null)
instance = new Main();
return instance;
}
You are actually creating a second instance of the applet, which is NOT the one that is on the screen, so when you call repaint, Swing goes, "no point, you're not even displayable" and does nothing.
Without any more context of you code, you may not even need getInstance, instead reference the current instance using Main.this instead.
You should also consider taking a look at Performing Custom Painting.
Top level containers like JAppelt are not double buffered, which involves more work to paint directly to them. Instead, move your application to be based on something like a JPanel and override it's paintComponent method instead.
Painting is also a complex, multi-layered scheme. You MUST call super.paintXxx in order to preserve the paint chain and prevent any possible issues.
I'm doing a simple game and I have a class that I call GameLoop and inside the run() method the code is passing the reference of the canvas to a method that draws a circle. This code is just some testcode.
My question is how do I pass the reference of the canvas if I want to create several sprite objects(classes) with a circle and put them in a list when the game starts? I have done this before, but then I used bitmap in each sprite object and just passed a reference of the image, but in this case I'm not sure how I would do since the canvas is inside the run() method, and I want to create my sprite objects in a method like initializeGameObjects() once in the beginning. I hope my question isn't unclear!? Preciate some help! Thanks!
// Game loop ---------------------------------------
#Override
public void run() {
// TODO Auto-generated method stub
while (gameRunning) {
if (!surfaceHolder.getSurface().isValid())
continue;
canvas = surfaceHolder.lockCanvas();
// Call method to draw objects on screen
drawObjects(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
// End game loop ---------------------------------------
// Method that draw everything on canvas
private void drawObjects(Canvas canvas) {
// Test
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.WHITE);
// Clear screen with black color
canvas.drawRGB(0, 0, 0);
// Draw a circle
canvas.drawCircle(100, 100, 100, paint);
}
I would implement Runnable and construct a Thread. This way you can have a "setup" method which ultimately calls a "start" method. The "start" method would draw to the canvas before starting the thread.
This is actually better practice for this kind of stuff. You may want to init stuff before starting a thread.
public class Game implements Runnable {
private Thread thisThread = new Thread(this);
private boolean gameRunning = false;
public void startGame() {
gameRunning = true;
//Draw to canvas
thisThread.start();
}
#Override
public void run() {
while(gameRunning) {
//Draw other objects
}
}
}
Define an interface:
public interface Renderable{
public void renderToCanvas(Canvas c);
}
Define a List of Renderables:
private List<Renderable> sprites = new ArrayList<Renderable>();
create methods to add/remove sprites, make sure they are synchronized:
public synchronized void addSprite(Renderable r){
sprites.add(r);
}
public synchronized void removeSprite(Renderable r){
sprites.remove(r);
}
This is drawing method, also synchronized, so you don't end up drawing and modifying sprite list at same time. This is either called in a loop by other thread, or a view's onDraw() method:
public synchronized void draw(Canvas c){
// lock canvas if needed
// common drawing code
for(Renderable r :sprites){
r.renderToCanvas(c);
}
// release canvas if locked
}
Now, you are free to define any type of sprite, Circle, Square or whatever,
public class Circle implements Renderable{
private Paint mPaint;
private float mRadius;
public Circle(Paint p, float r){
mPaint = p;
mRadius = r;
}
#Override
public void renderToCanvas(Canvas c){
c.drawCircle(100, 100, mRadius, mPaint);
}
}
and add it to sprite list:
addSprite(new Circle(new Paint(),20f));
Note: I used synchronized for simplicity, but there are better alternatives to it like atomic flags.These are required only if you want to add/remove sprites while game loop is running. If you setup all sprites before starting game loop, this is not a problem.
Of all these methods what's being run and in what order??
I guess the first question to ask is whats being run first?
And why does th.start() start run()?
import java.applet.*;
import java.awt.*;
import javax.swing.JFrame;
public class BallApplet extends Applet implements Runnable {
int x_pos = 10;
int y_pos = 100;
int radius = 20;
private Image dbImage;
private Graphics dbG;
public void init() {
// setBackground(Color.BLUE);
}
public void start() {
Thread th = new Thread (this);
th.start();
}
public void stop() {}
public void destroy() {}
public void run() {
// 20 second delay per frame refresh (animation doesn't
// need to be perfectly continuous)
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
while (true) {
x_pos++;
repaint();
try {
Thread.sleep(20);
}
catch (InterruptedException ex) {
System.out.println("Caught!");
}
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
}
}
public void update(Graphics g) {
// implements double buffering
// drawing on doublebufferImage, note the dbG=dbImage.getGraphics(), so everything dbG.whatever() is
// drawing on the Image's graphics which is later drawn with g.drawImage()
// initialize buffer
if (dbImage == null) {
dbImage = createImage (this.getSize().width, this.getSize().height);
dbG = dbImage.getGraphics();
}
// clear screen in background
dbG.setColor(getBackground()); // gets background color
dbG.fillRect(0, 0, this.getSize().width, this.getSize().height);
// draw elements in background
dbG.setColor(getForeground());
paint(dbG);
// draw image on the screen
g.drawImage(dbImage, 0, 0, this);
}
public void paint(Graphics g) {
g.setColor(Color.RED);
g.fillOval(x_pos-radius, y_pos-radius, 2*radius, 2*radius);
}
}
The init() and start() methods are invoked first.
That in turn creates a Thread and starts that thread, which causes this class's run() method to be invoked.
The paint() method is invoked by Swing independently in the GUI event handling thread, if Swing detects that the applet needs to be redrawn.
I note that the class's main run() method also repeatedly calls repaint(). That explicitly tells the GUI thread to invoke update().
The browser or Applet viewer first calls
init() method to inform this applet that it has been loaded into the system.
then after init start() method gets called. For more see the Applet class docs.
Inside start() method there is a call to th.start(). That means start() the thread execution
That will cause the run() to get invoked
From the Life Cycle of an Applet section of The Java Tutorials, the Applet's following methods are called in order:
init()
start()
stop()
destroy()
In addition, the code implements the Runnable interface, so the BallApplet's run() method is also executed after a new Thread (here, called th) is run by calling the th.start() method. (Calling the Thread.start() method starts a new thread and calls its run() method.)
The Defining and Starting a Thread section from The Java Tutorials has more information on Runnable and Thread and how threads are started in Java.
The run() method contains a call to repaint(), and this is an app-triggered update, it will call the BallApplet's update(Graphics g) method. In addition, the system-triggered repaint will trigger the paint(Graphics g) method.
For more information about repainting in AWT, refer to Painting in AWT and Swing. For information on system- and app-triggered painting, see the section on System-Triggered vs. App-Triggered Painting.
See Thread.start().
public void start()
Causes this thread to begin execution;
the Java Virtual Machine calls the run
method of this thread.
The result is that two threads are
running concurrently: the current
thread (which returns from the call to
the start method) and the other thread
(which executes its run method).
It is never legal to start a thread
more than once. In particular, a
thread may not be restarted once it
has completed execution.