How to restart screen in LibGDX using Init() methods? - android

I created a simple game in LibGDX that has multiple screens. I want to restart a certain screen after touching a restart button but I'm not sure how to do that. I made some research about this and all answers lead to not loading my assets in show() but rather in an init() method that I am not really familiar with. I want to know how can I restart a screen using this init() method. As of now I put most of my initialization in the constructor and some were initialized in methods such as the restartButton() method. Any other corrections or improvements in my code will be highly appreciated. Here is a snippet of my code:
public class LevelOneScreen implements Screen {
public MainShooter app;
private Stage stage;
private Stage stageCoin;
private Stage stageScore;
private Stage stageEnemies;
private Stage stageFX;
private Stage stageButton;
public Image aImage;
public Image bImage;
public Image cImage;
public Image dImage;
public Array<AntAnimation> AntAnimate;
public Array<CrumbAnimation> CrumbAnimate;
public Array<CoinAnimation> CoinAnimate;
public Texture firstTex;
public Texture secTex;
public Texture thirdTex;
public Texture fourthTex;
public LevelOneScreen(final MainShooter app){
this.app = app;
this.stage = new Stage(new StretchViewport(app.screenWidth, app.screenHeight, app.camera));
this.stageCoin = new Stage(new StretchViewport(app.screenWidth, app.screenHeight, app.camera));
this.stageScore = new Stage(new StretchViewport(app.screenWidth, app.screenHeight, app.camera));
this.stageEnemies = new Stage(new StretchViewport(app.screenWidth, app.screenHeight, app.camera));
this.stageFX = new Stage(new StretchViewport(app.screenWidth, app.screenHeight, app.camera));
this.stageButton = new Stage(new StretchViewport(app.screenWidth, app.screenHeight, app.camera));
AntAnimate = new Array<AntAnimation>();
CrumbAnimate = new Array<CrumbAnimation>();
CoinAnimate = new Array<CoinAnimation>();
restartButton();
levelOneBackground();
coinScore();
}
public void show() {
inputMultiplexer = new InputMultiplexer();
inputMultiplexer.addProcessor(stageCoin);
inputMultiplexer.addProcessor(stageScore);
inputMultiplexer.addProcessor(stageEnemies);
inputMultiplexer.addProcessor(stageFX);
inputMultiplexer.addProcessor(stageButton);
Gdx.input.setInputProcessor(inputMultiplexer);
}
public void levelOneBackGround(){
//code for the background
}
public void coinScore(){
//code for coinScore
}
public void restartButton(){
firstTex = MainShooter.manager.get("button.png", Texture.class);
aImage = new Image(firstTex);
stageButton.addActor(aImage);
aImage.addListener(new ActorGestureListener() {
public void touchDown(InputEvent event, float x, float y, int pointer, int button) {
// Creating new LevelOneScreen somehow affects the fps of my game.
app.setScreen(new LevelOneScreen(app));
}});
}
//some other codes like dispose, hide, etc.
}
This edit is a response to an answer provided by IronMonkey which seems to solve the problem but needs additional code. The problem here is that whenever the game is restarted, the actors from the restarted screen is on top of the actors on the previous screen(the screen before restarting). Here is a new snippet of my code showing how to restart the game using my GameInit():
public void GameInit(){
levelOneBackground();
scoreInt = 0;
coinInt = 0;
}
public void levelOneBackground(){
Runnable runAntAct1 = new Runnable(){
public void run(){
antAct1();
}
};
Runnable runAntAct2= new Runnable(){
public void run(){
antAct2();
}
};
Runnable runAntAct3 = new Runnable(){
public void run(){
antAct3();
}
};
Runnable runCoins = new Runnable(){
public void run(){
coinScattered();
}
};
levelOneTexture = CoinAssets.manager.get("woodenTable.png",Texture.class);
levelOneImage = new Image(levelOneTexture);
levelOneImage.setSize(app.screenWidth,app.screenHeight);
levelOneImage.setPosition(0,0);
stage.addActor(levelOneImage);
levelOneImage.addAction(sequence(run(runAct1),delay(2f),run(runAct2),delay(2f),run(runAct3),delay(2f),run(runCoins)));
}
public void restartButton(){
//some code for the position, size, texture of the button.
restartButton.addListener(new ActorGestureListener() {
public void touchDown(InputEvent event, float x, float y, int pointer, int button) {
GameInit();}});
}
public void antAct1(){
for(int i = 1; i < 4; i++){AntAnimate.add(new AntAnimation(app));}
AntAnimate.get(1).setPosition(x,y);
stageEnemies.addActor.(AntAnimate.get(1));
AntAnimate.get(1).addAction(//some actions);
AntAnimate.get(2).setPosition(x,y);
stageEnemies.addActor.(AntAnimate.get(2));
AntAnimate.get(2).addAction(//some actions);
AntAnimate.get(3).setPosition(x,y);
stageEnemies.addActor.(AntAnimate.get(3));
AntAnimate.get(3).addAction(//some actions);
}
public void antAct2(){
//almost the same with antAct1()
}
public void antAct3(){
//almost the same with antAct1()
}
public void coinScattered(){
//almost the same with antAct1()
}

show() is called automatically when the screen is first shown.
The init() method is one you create yourself.
You can call it whatever you want.
You should not load all the assets in init() just set everything up, score = 0, player.setPosition(0,0) etc.
So when you first call init() you set all variables. When you call init() again you set them again. (reset)
Efter edit of question:
In the methods called from GameInit() you load files that are already loaded and add actors to stages where those actors are already there and therefore now overlapping. You also add actions and create objects already created.
All GameInit() should do is set values for those already created objects.
So basically:
private void GameInit(){
AntAnimate.get(1).setPosition(x,y);
AntAnimate.get(2).setPosition(x,y);
AntAnimate.get(3).setPosition(x,y);
levelOneImage.setPosition(0,0);
scoreInt = 0;
coinInt = 0;
}
Loading and creation of objects should not be done more than once. Try just adding my GameInit() method, call it GameReset() and call it from your reset button.

Related

How to call on activity method from view?

I have done I game in which I want to call on a method I have in a view from another view. I figured I would somehow have to send the "first view" into the "second view" through the my MainActivity in order for the second view to be able to call on the first view methods. However, I couldn't come up with any way of sending in the first view to the second view through my MainAcitivity, so I decided to change tactics. I now tried to have a function in my MainActivity to handle the interection between the views, but once again I was not able to call on the method from the second View.
Therefore my question is how do you send a view into another view through an Activity, or If that's not possible how do you call on an activity method through a view?
Here is the code (I added some comments to better show the problem I'm having):
public class MainActivity extends AppCompatActivity {
private FishView gameView;
private SmallBall smallBall ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RelativeLayout screen = findViewById(R.id.gameScreen);
gameView = new FishView(this);
smallBall = new SmallBall(this);
screen.addView(gameView); // first view
screen.addView(smallBall); //second view
}
//this is the method I want to reach through the View
public void handleAvoidedBall(){
gameView.avoidedBall();
}
}
public class SmallBall extends View {
private final Bitmap sodaCan;
private final static long smallBallPeriod = 60;
private final Handler handler = new Handler();
public SmallBall(Context context) {
super(context);
Paint smallBall = new Paint();
smallBall.setColor(Color.GRAY);
smallBall.setAntiAlias(false);
resetBall();
sodaCan = BitmapFactory.decodeResource(getResources(),R.drawable.sodacan);
Timer movementTimer = new Timer();
movementTimer.scheduleAtFixedRate(smallBallTask, 0, smallBallPeriod);
}
private final TimerTask smallBallTask = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
#Override
public void run() {
invalidate();
if (isBallLanded()){
//Here I want to call on a handleAvoidedBall() in MainActivity
//OR simply have gameView here if possible
// gameView.avoidedBall();
//OR
//SomeMainAcitvityObject.handleAvoidedBall();
}
}
});
}
};
#Override
protected void onDraw(Canvas canvas) {
..... //Do stuff}
}
So as I hopefully have explained somewhat decent now, I'm wondering how to either send gameView into the SmallBall view OR how to call on handleAvoidedBall() in MainActivity from the SmallBall view?
Thank you for your time and hope you have a wonderful day!
Your best option would be to define a listener that you would set on the SmallBallView.
Define the listener:
public interface BallListener {
void onAvoided(SmallBall ball);
}
And then inside your SmallBall class, you would have this method:
public void setListener(BallListener listener){
this.listener = listener;
}
And then call this in your activity, after you've instantiated the SmallBall class:
smallBall.setListener(new SmallBallListener(){
#Override
public void onAvoided(SmallBall ball){
// Do stuff here
}
})
As #LukeWaggoner mentioned, you should consider using listeners instead of making view static in your activity.
You told us, that you'd like to add more than one SmallBall views, so I figure that you don't want to write a listener's code for each of them.
It is easily doable with MainActivity implementing SmallBallListener.
Listener:
public interface SmallBallListener {
void onAvoidedBall();
}
SmallBall class:
public void setListener(SmallBallListener listener){
this.listener = listener;
}
MainActivity:
public class MainActivity extends AppCompatActivity implements SmallBallListener {
private FishView gameView;
private SmallBall smallBall ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RelativeLayout screen = findViewById(R.id.gameScreen);
gameView = new FishView(this);
screen.addView(gameView); // first view
// Add 10 small ball views
for(int i = 0; i < 10; i++) {
SmallBall ball = new SmallBall(this);
ball.setListener(this); // MainActivity is a listener here, so each ball has the same listener code
screen.addView(ball);
}
}
//this is the method I want to reach through the View
public void handleAvoidedBall() {
gameView.avoidedBall();
}
#Override
public void onAvoidedBall() { // this is the SmallBallListener method
this.handleAvoidedBall();
}
}
So whichever SmallBall view call listener.onAvoidedBall(), it will fire onAvoidedBall() method in MainActivity class.
Turns out all I had to do was to set:
private FishView gameView;
to:
public static FishView gameView;
And then simply use "MainActivity.gameView" in the SmallBall view. This gave me no additional warings either, so that was good also.

Android: Why do I have to use runOnUIThread() Here?

I am working on a simple game with graphics using Open GL. I want it so that when a certain event happens, the player gets a point, and this updates a textview overlaying the GLSurfaceView on the game activity.
Thanks in advance!
Here are the relevant parts of my code:
The Activity:
public class playActivity extends Activity {
private GLSurfaceView myGLView;
private Handler mGameHandler;
private TextView mScoreView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mScoreView = new TextView(this);
mScoreView.setTextColor(0xFF00FF00); // Green
string myZeroString = "0";
mScoreView.setText(myZeroString); //Starting score will always be 0
mGameHandler = new Handler(){
public void handleMessage(Message msg){
if (msg.what == MyGLRenderer.GAME_SCORE_FLAG) {
int score = msg.arg1;
mScoreView.setText(Integer.toString(score));
}
}
};
myGLView = new MyGLSurfaceView(this);
setContentView(myGLView);
addContentView(mScoreView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
private void updateScoreBoard(int score){
mScoreView.setText(Integer.toString(score));
}
class MyGLSurfaceView extends GLSurfaceView{
private final MyGLRenderer myRenderer;
public MyGLSurfaceView(Context context){
super(context);
// Create an OpenGL ES 2.0 context
setEGLContextClientVersion(2);
myRenderer = new MyGLRenderer(context);
myRenderer.initiateHandler(mGameHandler);
// Set the Renderer for drawing on the GLSurfaceView
setRenderer(myRenderer);
setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
}
}
}
And the GlSurfaceView.Renderer:
public class MyGLRenderer implements GLSurfaceView.Renderer{
public static final int GAME_SCORE_FLAG = 1;
private Handler mGameHandler = null;
private int mScore = 0;
public void initiateHandler(Handler handler){
mGameHandler = handler;
}
...
#Override
public void onDrawFrame(GL10 unused){
if (scoreCondition){
mScore += 1;
if (mGameHandler != null) {
int flag = MyGLRenderer.GAME_SCORE_FLAG;
mGameHandler.dispatchMessage(Message.obtain(mGameHandler, flag, 2));
}
}
}
}
When I run this, it gives me the error:
"android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views."
However, in the top chunk of code, when I replace
int score = msg.arg1;
mScoreView.setText(Integer.toString(score));
with
final int score = 3;
runOnUiThread(new Runnable() {
#Override
public void run() {
mScoreView.setText(Integer.toString(score));
}
});
I no longer get the error and it works. I thought that in the first construction that when I called setText that it would have to be performed on the UI thread, but Android Studio thinks differently. Can anyone explain this to me? Thanks!
Change:
mGameHandler.dispatchMessage(Message.obtain(mGameHandler, flag, 2));
to:
mGameHandler.sendMessage(Message.obtain(mGameHandler, flag, 2));
Calling dispatchMessage() will cause the message to be delivered on the current thread (which is the GL thread in your case.)

Android TableLayout with over 1000 rows loading very slowly

I'm looking for a way to speed up the creation of a TableLayout with over 1000 rows. Is there a way to create a TableLayout entirely on a separate thread or a way to speed it up?
Here is my method that is creating the table:
private void setTable()
{
final Activity activity = this;
final Handler handler = new Handler();
new Thread(new Runnable()
{
#Override
public void run()
{
for (int x = 0; x < rooms.size(); x++)
{
final int inx = x;
handler.post(new Runnable()
{
#Override
public void run()
{
Methods.createRow(table, rooms.get(inx), null, activity);
TableRow row = (TableRow)table.getChildAt(inx);
row.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View arg0)
{
if (arg0.getTag() != null && arg0.getTag().getClass() == Integer.class)
select((Integer)arg0.getTag());
}
});
}
});
}
}
}).start();
}
I was hoping that using a Handler would at least allow the new Activity to appear before the table was created. The application seems to freeze up for a few seconds when creating tables with a lot of rows. setTable() is being run in my Activity's onStart() method.
Methods.createRow adds a row to the end of the TableView that is passed in.
Edit:
After deciding to try out a ListView, I got much better results with a lot less code.
private void setTable()
{
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, roomNames);
table.setAdapter(adapter);
table.setOnItemClickListener(new OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3)
{
select(arg2);
}
});
}
First things first.
Why your app freezes:
Handler works like a queue, it queues every post you made and than execute it serially in your main thread.
But the main problem is the amount of data you are trying to show at once, but it is easily solved with an Adapter, you probably can use some default Component for solve this, like ListView or GridView, you can make your custom rows to work around the columns maybe.
Just from guessing on the method name, it seems this line may be slow to run on the main/UI thread:
Methods.createRow(table, rooms.get(inx), null, activity);
I would suggest separating all the heavy database work and UI work with AsyncTask, so it might look something like this:
private OnClickListener rowClickListener = new OnClickListener()
{
#Override
public void onClick(View arg0)
{
if (arg0.getTag() != null && arg0.getTag().getClass() == Integer.class)
select((Integer)arg0.getTag());
}
};
private void setTable()
{
final Activity activity = this;
final Handler handler = new Handler();
new AsyncTask<Void, Void, List<TableRow>>() {
#Override
protected List<TableRow> doInBackground(Void... params) {
// Do all heavy work here
final List<TableRow> rows = new ArrayList<TableRow>(rooms.size());
for (int x = 0; x < rooms.size(); x++)
{
Methods.createRow(table, rooms.get(x), null, activity);
rows.add((TableRow)table.getChildAt(x));
}
return rows;
}
#Override
protected void onPreExecute() {
// maybe show progress indication
}
#Override
protected void onPostExecute(List<TableRow> result) {
// Do all UI related actions here (maybe hide progress indication)
for (final TableRow row : result) {
row.setOnClickListener(rowClickListener);
}
}
};
}
Since I can't tell what is in some methods, you'll just need to ensure you've tried to optimize as best as possible in Methods.createRow(...) and move all the UI related work to the onPostExecute(...) method.
You are stacking the post with all the actions at once. So it same as not using a thread at all as your main thread doing all the work. Try changing to postDelay, and for each room index give it 1 millisecond or more.

Add AnimationDrawable (PNG Sequence) to Canvas?

I have started venturing into a bit of Canvas stuff and managed to add some statis PNG files to it.
Now I'd like to load in a PNG sequence, which I believe is an AnimationDrawable (rather then Bitmap)
I have written the XML file for the animation but then I am stumped.
I cannot find any examples of people adding PNG sequences to a Canvas object.
here is sample:
public class MainActivity extends Activity {
private static final int FRAME_DELAY = 200; // in ms
private ArrayList<Bitmap> mBitmaps;
private final AtomicInteger mBitmapIndex = new AtomicInteger();
private View mView;
private Thread mThread;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// load resources
mBitmaps = new ArrayList<Bitmap>();
for(int resId : new int[]{
// resource ids here
R.drawable.ic_launcher,
R.drawable.ddms_128,
R.drawable.ddms_icon
}){
mBitmaps.add(BitmapFactory.decodeResource(getResources(), resId));
}
// create View and implement 'draw'
ViewGroup root = (ViewGroup) findViewById(R.id.root);
root.addView(mView = new View(this){
#Override
public void draw(Canvas canvas) {
canvas.drawBitmap(mBitmaps.get(Math.abs(mBitmapIndex.get() % mBitmaps.size())), 10, 10, null);
super.draw(canvas);
}
});
}
#Override
protected void onStart() {
super.onStart();
mThread = new Thread(){
#Override
public void run() {
// wait and invalidate view until interrupted
while(true){
try {
Thread.sleep(FRAME_DELAY);
} catch (InterruptedException e) {
e.printStackTrace();
break; // get out if interrupted
}
mBitmapIndex.incrementAndGet();
mView.postInvalidate();
}
}
};
mThread.start();
}
#Override
protected void onStop() {
mThread.interrupt();
super.onStop();
}
}
activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</RelativeLayout>
Canvas allows to draw line/shapes/bitmaps.
You managed to draw one bitmap.
Animation is just drawing one bitmap from bunch of them.
Canvas does nothing with drawables.
The logic neither related to Canvas nor to drawables.
Draw the first frame, then use a Handler to post delayed Message that when handled moves to the next frame and calls invalidate().
What you need to do is:
Step #1. Override VisualizerView.verifyDrawable(Drawable who)
#Override
protected boolean verifyDrawable(Drawable who) {
return true;
}
Step #2. Modify MainActivity.addAnimationRenderer()
private void addAnimationRenderer() {
final AnimationDrawable anim = (AnimationDrawable) getResources().getDrawable(R.drawable.png1);
AnimationRenderer animRenderer = new AnimationRenderer(anim);
mVisualizerView.addRenderer(animRenderer);
anim.setCallback(mVisualizerView);
mVisualizerView.post(new Runnable() {
#Override
public void run() {
anim.start();
}
});
}
Step #3. Modify AnimationRenderer: delete
mBitmap.setCallback(null);
mBitmap.start();

android line intersect with itself

Hello friends,
I draw a line using canvas.drawPath(mPath, mPaint); method.
but having some problem, i want to get whether line intersect with itself or not.
so please help.
thanks in advance
For better performance and UI response create a separate Thread with a Handler and Looper. Also use a HashSet to store the path which tells you of an intersection when you add a position since add() returns a boolean.
Here is some code which should hopefully give better performance.
public class MainActivity extends Activity {
private Handler mHandler;
private String position, lastPosition;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
#Override
protected void onResume() {
super.onResume();
Tracker t = new Tracker();
t.start();
}
#Override
protected void onPause() {
if (mHandler != null)
mHandler.getLooper().quit();
super.onPause();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_MOVE:
position = event.getX()+","+event.getY();
if (position.equals(lastPosition)) break;
if (mHandler != null) {
Message msg = Message.obtain();
msg.obj = position;
mHandler.sendMessage(msg);
}
lastPosition = position;
break;
}
return true;
}
private class Tracker extends Thread {
HashSet<String> path = new HashSet<String>();
#Override
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
String position = String.valueOf(msg.obj);
if (!path.add(position))
Log.w("Intersection", position);//Handle the intersection
}
};
Looper.loop();
}
}
}
save the coordinates from events
#Override
public boolean onTouchEvent(MotionEvent event) {
event.getX();event.getY()`
}
to a Hashmap and compare
I think you're best bet would be to:
1) Keep one path representing what's currently on the canvas, and every time you use canvas.drawPath(nextPath), also add it to your global path and do something like globalPath.addPath(nextPath)
2) Take a look at this post: Collision detection with bitmaps on SurfaceView's canvas in Android. It seems that you should be able to compare the globalPath to the nextPath and tell if they ever collide.
3) If instead you wanted to just know if a single path collides with itself and don't care about adding new paths or anything, we'd need more information on how you're drawing this path. With lines, arcs, circles??

Categories

Resources