Is it possible in LibGDX's `scene2d` API to have the same Actor instance in multiple Stages? - libgdx

I am making a program using the amazing libGDX+scene2d API and I structured it as follows:
I have a single MyGame instance, holding a single PolygonSpriteBatch instance.
There is an abstract MyScreen class, holding a MyStage class (see below)
Then there are lots of different screen classes that inherit from MyScreen, and instantiate each other at will.
(in all cases, removing the "My" gives you the name of the respective library class that it extends)
This model worked fine, until I encountered some problems to perform actions between screens using the Action system. I decided then that it would be a good idea to have a single OmnipresentActor belonging to MyGame that, as the name says, is present in every scene. So I modified MyStage to look more or less like this:
public class MyStage extends Stage {
public MyStage(MyGame g) {
super(new FitViewport(MyGame.WIDTH, MyGame.HEIGHT), g.batch);
addActor(game.omnipresentInvisibleActor);
}
#Override
public void clear() {
unfocusAll();
getRoot().clearActions();
getRoot().clearListeners();
removeActorsButNotListenersNorActions();
}
public void removeActorsButNotListenersNorActions() {
for (Actor a : getActors()) if (a.getClass()!= OmnipresentInvisibleActor.class) a.remove();
}
It followed a painful debugging phase, until I found out the following:
public PresentationScreen(MyGame g) {
// super() call and other irrelevant/already debugged code
System.out.println("PRINT_BEFORE: "+ stage.getActors().toString()); // OmnipresentActor is there
mainMenuScreen = new MainMenuScreen(game);
System.out.println("PRINT_AFTER: "+ stage.getActors().toString()); // OmnipresentActor is not there anymore, but was added to the mainMenuScreen
the "PRINT_BEFORE" statement shows that the stage holds the omnipresentActor. In "PRINT_AFTER" it isn't there anymore, whereas mainMenuScreen is indeed holding it. So my question, now more precise:
does scene2d prevent this to happen, or am I doing something wrong here?
Answers much appreciated! Cheers

An actor can only be a member of one stage: Thanks to #Tenfour04 for confirming that. The explanation is quite clear after doing a little research:
Stage.addActor() looks like this:
(here the github code of Stage.java)
/** Adds an actor to the root of the stage.
* #see Group#addActor(Actor) */
public void addActor (Actor actor) {
root.addActor(actor);
}
whereas root is simply initialized as a group in the Stage constructor: root = new Group();.
And Group.addActor() looks like this:
(here the github code of Group.java)
/** Adds an actor as a child of this group. The actor is first removed from its parent group, if any. */
public void addActor (Actor actor) {
if (actor.parent != null) actor.parent.removeActor(actor, false);
children.add(actor);
actor.setParent(this);
actor.setStage(getStage());
childrenChanged();
}
So in the tree first lines is the answer: when creating the new stage, if the actor to add already has a parent, it is removed from its current parent. So, There are two possible solutions to the problem I enounced:
SOLUTION 1: Override addActor removing the if statement, or any other alteration of the library, which I'm not sure if it would work. I rather think this could be very problematic, for instance it could prevent the stages from disposing correctly
SOLUTION 2: Change the design so you don't need an omnipresent actor, nor changing/reimplementing the libraries. For the moment this is what I've done based on this answer, it isn't very clean but it works so far:
1) In the MyScreen class added the following fields:
private boolean watchingTemp;
private Actor watchActorTemp;
private Action actionTemp;
2) Then added this method:
public void addActionOnStageAfterActorEndsHisActions(Actor actor, Action action) {
watchActorTemp = actor;
actionTemp = action;
watchingTemp = true;
}
3) then in the render method, I added the following:
if (watchingTemp && !watchActorTemp.hasActions()) {
watchingTemp = false;
stage.addAction(actionTemp);
}
4) finally, when wishing to perform an action at a screen transition (and eventually disposing the first one), you can do something like this: I use something similar when clicking on a door between screens
public void movePlayerTHENgotoNewScreen(float xPos, float yPos, whatever else...) {
game.player.walkToAnyPoint(xPos, yPos);
yourFavoriteScreen.addActionOnStageAfterActorEndsHisActions(game.player, gotoNewScreen(wathever else...));
}
Hope it helps!

Related

AS3 MVC Design Multiple Controllers

So I'm reading a book about MVC and the author create a controller (PlayerController) and put some functions for keyboard use and some for mouse use. But he just comment out every keyboard use.
It gave me a idea to create 2 controllers, PlayerMouseController and PlayerKeyboardController so I can decide how to control the player changing one line. And if I can design this way, later I can add a AIController for monsters that use the same view and model but are controlled by AI and so on...
I have my model Player and it do the physics stuff. Now I want two controller, one for mouse and other for keyboard. So I create a PlayerMouseController and PlayerKeyboardController.
The PlayerMouseController has 2 functions: processUpdate() and processMouseDown()
The PlayerKeyboardController has 2 functions: processKeyDown() and processKeyUp()
I create the object like this:
_player = new Player();
_playerController = new PlayerMouseController(_player);
_playerView = new PlayerView(_player, _playerController, stage);
addChild(_playerView);
If I want to change the controller I can just change the _playerController line for this:
_playerController = new PlayerKeyboardController(_player);
And it works fine... But I dont know if the design I use is fine for a large project
To make this work I have to create a Controller class with nothing so I can extends the others controllers and my view can call all methods.
public class Controller
{
public function processKeyDown(e:KeyboardEvent):void
{
}
public function processKeyUp(e:KeyboardEvent):void
{
}
public function processUpdate(stage:Stage):void
{
}
public function processMouseDown(e:MouseEvent):void
{
}
}
In my view (PlayerView) I accept any Controller:
public function PlayerView(model:Player, controller:Controller, stage:Stage)
{
_model = model;
_controller = controller;
_stage = stage;
}
and I decide what to use based on its type:
if (_controller is PlayerKeyboardController)
{
_stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
_stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
}
else if (_controller is PlayerMouseController)
{
_model.addEventListener(Model.UPDATE, onUpdate);
_stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
}
// EVENTS PlayerKeyboardController
private function onKeyDown(e:KeyboardEvent):void
{
_controller.processKeyDown(e);
}
private function onKeyUp(e:KeyboardEvent):void
{
_controller.processKeyUp(e);
}
// EVENTS PlayerMouseController
private function onUpdate(e:Event):void
{
_controller.processUpdate(_stage);
}
private function onMouseDown(e:MouseEvent):void
{
_controller.processMouseDown(e);
}
So... this is a good idea? How should I improve??
I think as your project evolves you'll soon hit a bottleneck with this kind of approach. I'd suggest creating a general IEntityController interface (don't be afraid of the word 'entity', it just shows that this is a game object controller, something that is parent for example of hero, enemy, etc.).
Then I'd create two separate implementations of this interface: BasicMouseController and BasicKeyboardController, so that I'd have two branches of these controllers with basic logic. If you need additional functionality for the Hero, you'd create a HeroMouseController class which would extend the BasicMouseController class and would have the advantage of calling super methods and adding the extended functionality easily.
You'd still have the benefit of passing different controllers to the PlayerView class as it's constructor would receive an IEntityController as a parameter, meaning anything implementing this class can be passed in.
There are many approaches for problems like this and StackOverflow is usually not meant to give these kind of answers, as every answer to these kind of questions is subjective and this website more fit for Problem/Solution kind of posts.

Graphics plain 2D objects are not rendered while an action occurred

I am designing a game in Swing. Currently I am designing the maze for this game. The maze is generated by using Depth First Search algorithm. In my main JFrame, I have some JPanel. One JPanel, named mazePanel contains the maze. There are some other JPanel also, which contains the JButton for controlling. Following is the mazePanel code.
import java.awt.Graphics;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
public class MazePanel extends JPanel {
private MazeGenerator mazeGenerator;
private boolean startNewMaze = false;
public MazePanel() {
setBorder(BorderFactory.createTitledBorder("Maze"));
setToolTipText("This is the maze");
}
public void addNewMaze() {
startNewMaze = true;
mazeGenerator = new MazeGenerator();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (startNewMaze) {
mazeGenerator.generate(g);
startNewMaze = false;
}
}
}
There is one JButton, which calls the method mazePanel.addNewMaze() and set the Boolean startNewMaze to true. After setting the startNewMaze, maze should be generated. i.e. mazeGenerator.generate(g) is inside if() condition. Method mazeGenerator.generate(g) recursively draw the random maze. That is why I don’t want to run this method not more than once.
Up to this everything is looking fine. But while I am running the main JFrame and clicks on the JButton, maze is not rendered in the mazePanel. Sometimes when I minimize and maximize the JFrame, maze rendered (might be because of repaint() occur). Even if I comment mazeGenerator.generate(g) inside if() condition and put some g.drawString(). The string is not rendered while action performed (i.e.Pressing JButton).
Where is the problem? Please help.
Thank you.
So basically you have a JPanel which contains nothing, you call a method unknown to Swing and expects the paintComponent method is magically called when you change the state of a private field.
You already discovered that minimizing and maximizing again solves your problem due to a repaint. That should be sufficient information to know you have to trigger a repaint yourself when you press that button.
If you would have followed the suggestion from #kleopatra to, and I quote,
change the state and then trigger the revalidation/painting
you would already have solved your problem

AS3 add an image to an "extended" movie clip

In AS3 - I use a generic button handler to deal with on-click events on a movie clip object. For the last 4 hours I’ve been trying to add an image to this movie clip (see * * *) object.
The code (I cut and pasted a bit but this all compiles without any errors)
btPlay = new mcButtonPlay(this,"ClickMe",GameImage); // GameImage is an BitmapData object
public class mcButtonPlay extends navigationButtonHandler {
public function mcButtonPlay(Parent:MovieClip,Text:String,GameImage:BitmapData) {
super(Text);
if (GameImage != null) {
var ImageBitMap:Bitmap = new Bitmap(GameImage);
this.addChild(ImageBitMap); // * * * This doesn’t show
Parent.addChild(ImageBitMap); // Works just to test the image
}
}
}
public class navigationButtonHandler extends MovieClip {
public function navigationButtonHandler(Text:String) {
ChangeButtonTargetText(Text);
Parent.addChild(this);
}
}
public class navigationButtonHandler extends MovieClip {
public function navigationButtonHandler(Text:String) {
ChangeButtonTargetText(Text);
Parent.addChild(this); //<--------????
}
}
where does Parent come from in the above piece of code copied from the question? It would seem the navigationButtonHandler class never gets added to the stage because it doesn't get a Parent? So the extended class also is never added to the stage which would be why your image is never shown if you addChild it to your mcButtonPlay class.
Your extended constructor DOES pass a Parent parameter but the base class doesn't. That seems weird to me and should not compile. Or are you doing some static things behind the scenes?
And get to work on your capitalization style as mentioned in the comments. It's really a lot easier to read and find errors if you follow common conventions!
this.addChild(ImageBitMap); // * * * This doesn’t show
Parent.addChild(ImageBitMap); // Works just to test the image
With this code, ImageBitMap is being removed from 'this' and placed in 'Parent', so you will never see it in 'this'.
Remove that second line and tell me if it's still working.
Edit:
Are you adding btPlay to the stage or display hierarchy?
eg.
btPlay = new mcButtonPlay(this,"ClickMe",GameImage);
this.addChild(btPlay);
Managed to fix it by swapping the super and add child logic around:
public class mcButtonPlay extends navigationButtonHandler {
public function mcButtonPlay(Parent:MovieClip,Text:String,GameImage:BitmapData) {
if (GameImage != null) {
var ImageBitMap:Bitmap = new Bitmap(GameImage);
this.addChild(ImageBitMap); // * * * This doesn’t show
Parent.addChild(ImageBitMap); // Works just to test the image
}
super(Text);
}
}
No idea why though!

How to use Behavior class with Swing events?

I'm writing code for a 3D graph, and I want the scene graph to update when the user presses a JButton. I'm trying to use a Behavior class, but I can't find any information on using swing events to wake up the behavior. I would REALLY appreciate any help! Thank you!!
You can use a special behavior object which contains a queue of Runnables. You can then post runnables to the behaviour and wake it up. You will have to sort out proper synchronisation so the behaviour only goes to sleep when there are no more commands in the queue, but it should work.
Make the class into a singleton to be able to run Runnable's inside the BehaviorScheduler, analogous to the SwingUtilities.invokeLater() method.
public class ThreadTransferBehavior extends Behavior {
private final static int POST_ID = 9997;
private final WakeupOnBehaviorPost m_wakeupPost = new WakeupOnBehaviorPost(this, POST_ID);
private final Stack<Runnable> commands;
public synchronized void processStimulus(Enumeration i) {
while(!commands.isEmpty()) commands.pop().run();
wakeupOn(m_wakeupPost);
}
public synchronized void queueCommand(Runnable r) {
commands.push(r);
postId(POST_ID);
}
}

How to think "Tell, don't ask" in this simple example?

How would you adhere to the "Tell, don't ask" principle (henceforth "the principle") in the following simple scenario? In a Tetris game, I have Board, BlockGrid and Piece classes relevant to the following example:
public class Board
{
private var fallingPiece:Piece;
private var blockGrid:BlockGrid;
...
public function moveFallingPiece(xDirection:int, yDirection:int):void
{
blockGrid.movePiece(fallingPiece, xDirection, yDirection);
}
}
Once fallingPiece is placed in the bottom row of BlockGrid, it should no longer be the "fallingPiece". Am I right in that I'm not violating the principle with the following?
if(blockGrid.getPiecePosition(piece).y == 0)
{
fallingPiece = null;
}
But is that really different from this, which I think clearly violates the principle?
public function moveFallingPiece(xDirection:int, yDirection:int):void
{
if(blockGrid.getPiecePosition(piece).y > 0)
{
blockGrid.movePiece(fallingPiece, xDirection, yDirection);
}
else
{
fallingPiece = null;
}
}
I'm not assuming that I've designed these class relationships in the proper way to work with the principle. Please advice on an alternate design if that's what I'm missing.
EDIT, Proposed solution:
I went with the answers proposing "command feedback" via events. Board tells BlockGrid to move a piece. BlockGrid's movePiece method dispatches MOVED_TO or MOVE_FAILED events depending on the result, which Board can listen to and use to determine whether a piece has stopped falling. Please don't hesitate to provide feedback on this solution.
public class Board
{
...
public function Board()
{
...
blockGrid.addEventListener(PieceMoveEvent.MOVE_FAILED, onPieceMoveFailed);
...
}
public function moveFallingPiece(xDirection:int, yDirection:int):void
{
blockGrid.movePiece(fallingPiece, xDirection, yDirection);
}
public function onPieceMoveFailed(event:MovePieceEvent):void
{
if(event.instance == currentlyFallingPiece && event.fromPosition.y != event.toPosition.y)
{
currentlyFallingPiece = null;
}
}
I think, to better follow the Tell, Don't Ask principle, you should have blockGrid notifying your Board class when fallingPiece has reaches it's resting point. In both scenarios above, you are asking blockGrid if the piece's position.y == 0 in order to determine whether or not fallingPiece should be null. Instead, you want blockGrid to tell the Board class that fallingPiece.y has hit 0.
What you are looking for is Event driven programming. You need a Listener interface with a method called .event() and an Event interface to represent the events. Objects will register with other objects ( callbacks ) to the Listener interface.
when you create a Piece and Board they should implement the Listener interface. Then you can set the Board with registerListener(board); Then when things happen inside Piece it will loop thru all the registered listeners and call .event(event) on each. Same with the Board, call board.registerListener(piece) each time you create a new piece, as it decides things are happening it can tell all the registered listeners what has happened. Then you can tell a piece it is no longer falling by the Board object deciding this. Here is the obligitory Wikipedia entry.
I would expect a class representing each shape (without position information), a controller containing a shape, position and orientation, and another class representing the current resulting grid of "landed" shapes. The landed-grid would have a
testLanded(shape, shapePosition, orientation)
method which would be called before/after each move operation to decide if the shape is to join the landed grid or should move and stay as the falling piece.
I'm going on the idea of not giving data to objects that shouldn't really own that data - but I've never implemented Tetris...
You may need to rethink your design. Does Board really need to track the falling piece or should that belong to BlockGrid? Iron out who owns what behavior.
Keep position information on your Piece class and possibly have your Piece class hold an instance of the BlockGrid.
You can then try something like this in your Board class...
public function moveFallingPiece(xDirection:int, yDirection:int):void
{
blockGrid.moveFallingPiece(xDirection, yDirection);
}
Then in BlockGrid's moveFallingPiece method...
public function moveFallingPiece(xDirection:int, yDirection:int):void
{
fallingPiece.move(xDirection, yDirection);
}
In Piece's move method, add your logic...
public function move(xDirection:int, yDirection:int):void
{
setPosition(xDirection, yDirection);
if (getPosition().y <= 0)
{
blockGrid.setFallingPiece(null);
// this can bubble up to Board if need be
}
}
Not sure of all the power of AS3, but it would make sense to use abstractions here. (i.e., have your Piece class depend on ITrackFallingPieces instead of BlockGrid and have BlockGrid implement ITrackFallingPieces).
Good luck!