I am creating a Jewel like game for android, however my problem is that the game is very laggy, I tried creating an AsyncTask to reduce the pressure on the UIthread, it helped but it's still too laggy to play it, any tips on how I should change my code? I also tried creating Two different arrayList of ImageView to reduce time in the for loops : liste_rubies and liste_rubies_falling. Basically they have to stop falling once they touch another ruby and stack unto one another, it works but it's way too laggy past a certain number of rubies on the board. Thanks for your answers
public boolean TestColisions(ImageView Ruby){
boolean test = false;
Rect Rect_tempo = new Rect();
Ruby.getHitRect(Rect_tempo);
for(int y=0;y<liste_Rubies.size();y++){
if(liste_Rubies.get(y).getY() != Ruby.getY()){
Rect Rect_tempo2 = new Rect();
liste_Rubies.get(y).getHitRect(Rect_tempo2);
Rect_tempo2.set( Rect_tempo2.left, (int) (Rect_tempo2.top), Rect_tempo2.right, Rect_tempo2.bottom);
if (Rect.intersects(Rect_tempo2, Rect_tempo)) {
test = true;
liste_Rubies.remove(y);
if(Ruby.getY()<H10)
MessageVictoire();
}
}
}
return test;
}
//-----------------------------------------------------------------------------------------------------------------MoveDown V2 --
private class MoveDownV2 extends AsyncTask<String, Void, String> {
ArrayList<ImageView>liste_Rubies, liste_Rubies_falling;
public MoveDownV2(ArrayList<ImageView>liste_Rubies, ArrayList<ImageView>liste_Rubies_falling) {
this.liste_Rubies = liste_Rubies;
this.liste_Rubies_falling = liste_Rubies_falling;
String launch = doInBackground();
}
#Override
protected String doInBackground(String... params) {
MoveDown=new Timer();
MoveDown.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
for(int x = 0; x < liste_Rubies_falling.size(); x++){
float tempo = liste_Rubies_falling.get(x).getY();
if(liste_Rubies_falling.get(x).getY() < H10*7 && !TestColisions(liste_Rubies_falling.get(x))){
liste_Rubies_falling.get(x).setY(tempo+50);}
else{liste_Rubies.add(liste_Rubies_falling.get(x));
liste_Rubies_falling.remove(liste_Rubies_falling.get(x));}
}
LeCounter++;
if(LeCounter%17==0){
RubyPopUp();
}
}
}, 82, 82);
return null;
}
}
public void RubyPopUp() {
SpartanRuby.this.runOnUiThread(new Runnable() {
#Override
public void run() {
ImageView Ruby = new ImageView(context);
Ruby.setX((float) (L10*5));
Ruby.setY(0);
Ruby.setScaleType(ImageView.ScaleType.FIT_XY);
Ruby.setImageResource(R.drawable.rubies_rouge);
RubyTest = Ruby;
liste_Rubies_falling.add(Ruby);
liste_Rubies_total.add(Ruby);
((ViewGroup) layout_spartanruby).addView(Ruby);
SetSize(Ruby);
}
});
}
Related
I'm working on a little Android Game with libGDX.
The structure is as follows:
Main > Screen:Splashscreen > Screen:MainMenu > Screen: Settings or GameScreen
I have a Preference for the state of music(if running "music" is true).
If the music is true the music starts in the MainMenu.
Now the user should be abled to turn the music on and off in the Settings Screen.
To manage the music I made a class called MusicPlayer.
The Code:
MainMenu:
public class StartScreen implements Screen {
MusicPlayer musicPl;
Preferences scorepref;
boolean music;
(...)
#Override
public void show() {
(...)
scorepref = Gdx.app.getPreferences("Highscore");
musicPl = new MusicPlayer();
music = scorepref.getBoolean("music");
if(music){
musicPl.play();
}
}
#Override
public void dispose() {
musicPl.dispose();
}
}
Settings:
public class SettingScreen implements Screen {
Preferences scorepref;
//Setting Values
boolean tut = false;
boolean music = false;
boolean sounds = false;
int theme = 0;
//
MusicPlayer musicPl;
int touchX = 0;
int touchY = 0;
boolean touchD = false;
boolean touchU = false;
#Override
public void show() {
(handling Input and setting touchX,touchY,touchD and touchU)
(...)
musicPl = new MusicPlayer();
scorepref = Gdx.app.getPreferences("Highscore");
}
#Override
public void render(float delta) {
tut = scorepref.getBoolean("tut");
music = scorepref.getBoolean("music");
sounds = scorepref.getBoolean("sounds");
(...)
//if Touched
if(touchD == true && touchU == true){
touchD = false;
touchU = false;
//wo?
Vector3 posn = new Vector3(Gdx.input.getX(),Gdx.input.getY(), 0);
camera.unproject(posn);
if(){
(...)
} else if (posn.x >= -192 && posn.x <= 192 && posn.y <= 53 && posn.y >= -75) {
if(music){
music = false;
scorepref.putBoolean("music", music);
scorepref.flush();
musicPl.stop(); //Error line 206 is here
} else {
music = true;
scorepref.putBoolean("music", music);
scorepref.flush();
musicPl.play();
}
} (...)
} else if (posn.x >= -344 && posn.x <= -248 && posn.y <= 543 && posn.y >= 463) {
((Game) Gdx.app.getApplicationListener()).setScreen(new StartScreen());
}
//var speichern
scorepref.putBoolean("tut", tut);
scorepref.putBoolean("music", music);
scorepref.putBoolean("sounds", sounds);
scorepref.flush();
}
#Override
public void dispose() {
musicPl.dispose();
}
}
MusicPlayer:
public class MusicPlayer{
Music menuMusic;
boolean isrunning;
Preferences scorepref;
public void play (){
scorepref = Gdx.app.getPreferences("Highscore");
if(scorepref.getBoolean("running") == true){
} else {
menuMusic = Gdx.audio.newMusic(Gdx.files.internal("Theme.mp3"));
menuMusic.setLooping(true);
scorepref.putBoolean("running", true);
scorepref.flush();
menuMusic.play();
}
}
public void stop(){
scorepref = Gdx.app.getPreferences("Highscore");
scorepref.putBoolean("running", false);
scorepref.flush();
menuMusic.stop(); //Error line 33 is here
}
public void dispose(){
menuMusic.dispose();
}
}
My Problem:
When I am in the Settings Screen the tunring on and off is working fine.
When I turn the music on and go back to the MainScreen the music is still playing. So far so good.
But when I return to the SettingsScreen with running music and I want to turn it off the App crashes.
I think the crash is caused by the stop method, because the MusicPlayer doesn't know what to stop. But how can I tell him or how can i solve this problem with a different technique?
Thanks for helping.
P.S.
Here is the error I get, when I run the App on the desktop:
Exception in thread "LWJGL Application" java.lang.NullPointerException
at de.hatgames.canone.MusicPlayer.stop(MusicPlayer.java:33)
at de.hatgames.canone.SettingScreen.render(SettingScreen.java:206)
at com.badlogic.gdx.Game.render(Game.java:46)
at de.hatgames.canone.CanoneMain.render(CanoneMain.java:26)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:215)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:120)
CanoneMain.java:26 is just this:
super.render();
when you return to SettingScreen you create a new MusicPlayer instance each time.
musicPl = new MusicPlayer();
The Music member is only instanciated when play() is called:
menuMusic = Gdx.audio.newMusic(Gdx.files.internal("Theme.mp3"));
That is why menuMusic is null when you call stop() before calling play()
There are several solutions. You can make the MusicPlayer static and global accesable.
Or you can make sure MusicPlayer is only instanitated once like this:
if(musicPl == null) {
musicPl = new MusicPlayer();
}
But this will only work when you make sure that SettingsScreen is only instantiated once.
Good topics to read are probably singleton and factory pattern since this case will often occur when programming games.
Additionally using a Singleton for the music player as part of it being a global object will be helpful.
I am creating this app.
code of my onsingletapup.cs file
class SingleTapUp : Android.Views.GestureDetector.SimpleOnGestureListener
{
public override bool OnSingleTapUp(MotionEvent e) {
// Toast.MakeText(this,, ToastLength.Long).Show();
return true;
}
}
here is my mainactivity.cs
public class MainActivity : ActionBarActivity, View.IOnTouchListener
{
GestureDetector gestureDetector;
float _viewX;
float _viewY;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
PopulateListView(someList,anynumbertoshow)
}
private void QueueListView(Queue<FeedItem> feedItemsList, int count)
{
RelativeLayout rl = this.FindViewById<RelativeLayout>(Resource.Id.newsContainer);
if(rl.Visibility == ViewStates.Gone)
{
this.FindViewById<LinearLayout>(Resource.Id.newsList).Visibility = ViewStates.Gone;
rl.Visibility = ViewStates.Visible;
}
Paint layerPaint = new Paint();
layerPaint.AntiAlias = true;
layerPaint.FilterBitmap = true;
layerPaint.Dither = true;
// RelativeLayout parentLayout = (RelativeLayout)LayoutInflater.from(context).inflate(R.layout.myLayout, null);
rl.SetLayerType(LayerType.Hardware, layerPaint);
rl.SetClipChildren(false);
Random rnd = new Random();
//this.progressDialog.Dismiss();
for (int i = 0; i < count; i++)
{
FeedItem rss = theNewsQueue.Dequeue();
var viewObj = this.LayoutInflater.Inflate(Resource.Layout.NewTile, rl, false);
TextView tv = viewObj.FindViewById<TextView>(Resource.Id.textView2);
TextView link = viewObj.FindViewById<TextView>(Resource.Id.link);
link.Text = rss.Link;
tv.Text = rss.Title;
viewObj.Rotation = angle;
angle = rnd.Next(-3, 3);
viewObj.SetLayerType(LayerType.Hardware, layerPaint);
rl.AddView(viewObj);
gestureDetector = new GestureDetector(this, new SingleTapUp());
viewObj.SetOnTouchListener(this); //Here I am adding my listener to all my control
rl.SetLayerType(LayerType.Hardware, layerPaint);
theNewsQueue.Enqueue(rss);
rss = null;
}
}
public bool OnTouch(View v, MotionEvent e)
{
if (gestureDetector.OnTouchEvent(e))
{
//will detect a click and open in browser
return true;
}
else
{
int initialTouchX = 0, initialTouchY = 0;
int newx = 0;
var x = v.Left;
switch (e.Action)
{
case MotionEventActions.Down:
{
_viewX = e.GetX();
_viewY = e.GetY();
initialTouchX = (int)e.RawX;
initialTouchY = (int)e.RawY;
break;
}
case MotionEventActions.Up:
{
int lastX = (int)e.GetX();
int lastY = (int)e.GetY();
if ((x - newx) > 40)
{
//right Swipe
sendViewToBack(v);
}
else if ((newx - x > 40))
{
//left Swipe
sendViewToBack(v);
}
break;
}
case MotionEventActions.Move:
{
// click = false;
var left = (int)(e.RawX - _viewX);
newx = left;
var right = (int)(left + v.Width);
var top = (int)(e.RawY - _viewY);
var bottom = (int)(top + v.Height);
v.Layout(left, top, right, bottom);
break;
}
}
}
// _gestureDetector.OnTouchEvent(e);
return true;
}
public void sendViewToBack(View child)
{
var parent = (ViewGroup)child.Parent;
if (null != parent)
{
parent.RemoveView(child);
if(viewType==0)
parent.AddView(QueueListView (theNewsQueue), 0);
else
parent.AddView(QueueListView (theNewsQueue), parent.ChildCount-1);
}
}
}
Now my question is on some devices my current code is giving some abnormal behavior. Like even if I perform OnSingleTapUp() which is supposed to perform click operation but it is performing a move operation. My question is what is wrong with my code so that it is not working correctly. Any help will be greatly appreciated. Thank you.
The onTouch and onClick doesn't work together. In all the cases the onTouch is going to get the priority, in fact onClick in also sort of fine implementation of onTouch. If you want to have onClick sort of functionality, let go the original onClick and try to handle that in onTouch. You can take help of the GestureDetector.SimpleOnGestureListener in Xamarin. For an example to override the double tap you can do it like this
class MyDoubleTapListener : GestureDetector.SimpleOnGestureListener
{
public override bool OnDoubleTap(MotionEvent e)
{
//Your code here
return false;
}
}
and then in your activity
public class Test : Activity, View.IOnTouchListener
{
private GestureDetector _gestureDetector = null;
protected override void OnCreate (Bundle bundle)
{
_gestureDetector = new GestureDetector(new MyDoubleTapListener (this));
_editText.SetOnTouchListener(this);
}
public bool OnTouch(View v, MotionEvent e)
{
return _gestureDetector.OnTouchEvent(e);
}
}
GestureDetector also provides you other methods that you can overide to suit your need. Follow this, https://developer.xamarin.com/api/type/Android.Views.GestureDetector/
I couldn't get exactly what your are trying to do inside onTouch interface. Anyway there's some points you must know:
1) When you handle a touch event, onTouch method returns a boolean that indicates if event was consumed (true) or not (false). If you consume touch event related to click, click listener won't be triggered. So, make sure you are only consuming what is desired.
2) When you set listeners as onClick or onTouch to some view, it becomes clickable and touchable, respectively, if it wasn't. If you are setting this attributes to false in some part of your code make sure it's enabled again when you want to handle such events.
I'm an Android newbie developer and having an issue with a tiny Android app I'm developing which I suspect is related to timing of dynamic view creation. It's a little scorekeeper app and it has dynamically generated player buttons and text fields, defined in a Player class. Here's an excerpt of how I'm doing this:
public Player(String name, int score, int number, boolean enabled, RelativeLayout mainScreen, List<Button> presetButtonList, Editor editor, Context context) {
super();
this.name = name;
this.score = score;
this.number = number;
this.enabled = enabled;
this.editor = editor;
// Get the lowest child on the mainScreen
Integer count = mainScreen.getChildCount(), lowestChildId = null;
Float lowestChildBottom = null;
for (int i = 0; i < count; i++) {
View child = mainScreen.getChildAt(i);
if ((lowestChildId == null || child.getY() > lowestChildBottom) &&
!presetButtonList.contains(child)) {
lowestChildId = child.getId();
lowestChildBottom = child.getY();
}
}
playerNameText = (EditText) setTextViewDefaults(new EditText(context), name);
playerNameText.setSingleLine();
// playerNameText.setMaxWidth(mainScreen.getWidth());
// playerNameText.setEllipsize(TextUtils.TruncateAt.END); //TODO: Prevent names which are too long for screen
playerNameText.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable changedText) {
setName(changedText.toString());
updateScreen();
}
public void beforeTextChanged(CharSequence changedText, int start, int count, int after) {}
public void onTextChanged(CharSequence changedText, int start, int before, int count) {}
});
RLParams = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
if (lowestChildId != null) {
RLParams.addRule(RelativeLayout.BELOW, lowestChildId);
} else {
RLParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
RLParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
}
RLParams.setMargins(0, 10, 0, 10);
mainScreen.addView(playerNameText, RLParams);
the class further defines buttons and etc in a similar fashion, aligning tops with the name text view. I call this when a user clicks a button to add a player, and it works fine, displaying each player below the first on down the screen. The problem comes in when I'm loading a bunch of saved players at the start. Here's where i load the players:
public void loadData() {
RelativeLayout mainScreen = (RelativeLayout)findViewById(R.id.relativeLayout);
playerCount = savedData.getInt("playerCount", playerCount);
Player player;
String name;
playerList.clear();
for (int i=1; i<=playerCount; i++) {
name = savedData.getString("name" + i, null);
if (name != null) {
Log.v("name", name);
player = new Player(name, savedData.getInt("score" + i, 0), i, savedData.getBoolean("enabled" + i, false), mainScreen, presetButtonList, editor, this);
playerList.add(player);
player.updateScreen();
}
}
updateScreen();
}
Finally, I call the loadData() method when the app starts, here:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
savedData = this.getPreferences(Context.MODE_PRIVATE);
editor = savedData.edit();
presetButtonList.add((Button)findViewById(R.id.newGame));
presetButtonList.add((Button)findViewById(R.id.addPlayer));
presetButtonList.add((Button)findViewById(R.id.removePlayer));
loadData();
}
The result? When there are more than two players to load, all the players get loaded to the same spot, on top of Player 2.
I suspect somehow that the players are all being generated at the same time and thus all believing that the lowest player view is Player 1, and not checking each other. I've tried triggering the load later than onCreate() and it still happens. I also tried adding a 3 second sleep within the for loop of loadData() after each player loads to see if that helped, but no luck.
Am I making bad assumptions? What am I doing wrong and how might I fix it?
I suspect what is happening here is that you are attempting to position views that don't have IDs (it looks like you don't set them anywhere in your code). Dynamically created views don't automatically have IDs, so a call to getId() will return NO_ID (See documentation). If you want to use an ID you will have to set it yourself using View.setId().
It was a timing issue after all. I needed to wait for the main screen to load before I could add views to it. Unfortunately, it would never load until all activity on the UI thread was finished.
Thus, I needed threading. I ended up putting loadData() in a thread, like this:
new Thread(new loadDataAsync(this)).start();
class loadDataAsync implements Runnable {
MainActivity mainActivity;
public loadDataAsync(MainActivity mainActivity) {
this.mainActivity = mainActivity;
}
#Override
public void run() {
try {
Thread.sleep(700);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
loadData(mainActivity);
}
}
Then I created the players back on the UI thread after the initial sleep to give time for the UI to load.
runOnUiThread(new Runnable() {
Player player;
RelativeLayout mainScreen = (RelativeLayout)findViewById(R.id.relativeLayout);
#Override
public void run() {
player = new Player(savedData.getString("name" + playerNum, null), savedData.getInt("score" + playerNum, 0), playerNum, savedData.getBoolean("enabled" + playerNum, false), mainScreen, presetButtonList, editor, mainActivity);
playerList.add(player);
player.updateScreen();
mainScreen.invalidate();
}
});
Not the most elegant solution, but it works for now.
I do believe that answer should already be somewhere on so, but after some search I still cant figure out my problem..
public class cl1{
private List<Map> world_map = new ArrayList<Map>();
public List<Map> returnmap(){
return world_map;
}
public void addtomap(float x, float y, int type){
Map tile = new Map();
tile.x = x;
tile.y = y;
tile.type = type;
world_map.add(tile);
}
}
Activity class:
protected void onCreate(Bundle savedInstanceState) {
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View vw) {
List<Map> map = new ArrayList<Map>();
map = robot.returnmap();
for(int i=0;i<map.size();i++){
txt.setText(String.valueOf(i));
}
cl1.addtomap(x, y, 1);
x++;
y++;
}
This works fine: I can add to cl1.world_map with my method from activity class, and I can also get values from world_map from my activity class.
I have another class:
protected List<Map> strap = new ArrayList<Map>();
#Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
returnmap();
canvas.drawText(String.valueOf(strap.size()), 100, 120, paint1);
}
private void returnmap(){
strap = robot.returnmap();
}
Here I'm always getting value of 0, like the "List strap" would never update?
Your 'robot' variable is probably getting re-instantiated or is not even the same instance in your two classes, if that is not the case, keep in mind that in android there are many events under which you could get your fragments or activities re-instantiated and you should always save the state you intend to keep yourself
Background: I have a IOIO which I am using to measure the output from an photodiode, this is then converted into a digital output. I need to find the frequency at which the signal changes between 1 and 0. Someone gave me some code that I could use to measure the frequency, but I have no idea how to integrate it into my existing app. I know that the way I have implemented it will not work because the UI thread which updates the variable is waiting on the return from the other thread that calculates the frequency, so that thread will only get the value of diode when it starts running. So how do I make the frequency thread have a real-time value for diode and also after it has calculated the frequency pass it back to the UI thread to be displayed?
Here is my UI thread (FrequencyApp.java):
public class FrequencyApp extends IOIOActivity {
private TextView textView_;
private TextView textView2_;
private TextView textView3_;
private ToggleButton toggleButton_;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
textView_ = (TextView)findViewById(R.id.TextView);
textView2_ = (TextView)findViewById(R.id.TextView2);
textView3_ = (TextView)findViewById(R.id.FrequencyLabel);
toggleButton_ = (ToggleButton)findViewById(R.id.ToggleButton);
enableUi(false);
}
class Looper extends BaseIOIOLooper {
private AnalogInput input_;
private DigitalOutput led_;
volatile int diode;
private long frequency;
#Override
public void setup() throws ConnectionLostException {
try {
input_ = ioio_.openAnalogInput(31);
led_ = ioio_.openDigitalOutput(IOIO.LED_PIN, true);
enableUi(true);
} catch (ConnectionLostException e) {
enableUi(false);
throw e;
}
}
#Override
public void loop() throws ConnectionLostException {
try {
led_.write(!toggleButton_.isChecked());
float reading = input_.getVoltage();
if(reading > 1){
diode = 1;
} else {
diode = 0;
}
if(toggleButton_.isChecked()){
FrequencyThread frequencyTaskThread = new FrequencyThread();
frequencyTaskThread.setPriority(Thread.NORM_PRIORITY-1); //Make the background thread low priority. This way it will not affect the UI performance
frequencyTaskThread.start();
frequency = (long) frequencyTaskThread.run(diode);
frequencyTaskThread.stop();
}
setText(Float.toString(reading), Long.toString(diode), Long.toString(frequency));
Thread.sleep(100);
} catch (InterruptedException e) {
ioio_.disconnect();
} catch (ConnectionLostException e) {
enableUi(false);
throw e;
}
}
}
#Override
protected IOIOLooper createIOIOLooper() {
return new Looper();
}
private void enableUi(final boolean enable) {
runOnUiThread(new Runnable() {
#Override
public void run() {
toggleButton_.setEnabled(enable);
}
});
}
private void setText(final String str,final String str2,final String str3 ) {
runOnUiThread(new Runnable() {
#Override
public void run() {
textView_.setText(str);
textView2_.setText(str2);
textView3_.setText(str3);
}
});
}
}
Here is the thread for calculating the frequency(FrequencyThread.java:
public class FrequencyThread extends Thread {
public float run(int diode){
// Find frequency to the nearest hz (+/- 10%)
// It's assumed that some other process is responsible for updating the "diode"
// variable. "diode" must be declared volatile.
long duration = 1000; // 1 second
final int interval = 100; // sampling interval = .01 second
int oldState = diode;
int count = 0;
final long startTime = System.currentTimeMillis();
final long endtime = startTime + duration;
while (System.currentTimeMillis() < endtime) {
// count all transitions, both leading and trailing
if (diode != oldState) {
++count;
oldState = diode;
}
Thread.sleep(interval);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// find the actual duration
duration = System.currentTimeMillis() - startTime;
// Compute frequency. The 0.5 term is because we were counting both leading and
// trailing edges.
float frequency = (float) (0.5 * count / (duration/1000));
return frequency;
}
}
The way you have it set up easiest would probably be to use a Handler to send messages from the FrequencyThread to the main thread. The way that is probably preferred to use an AsyncTask to abstract away the Thread / Handler and make the whole situation a bit easier to deal with. Doing so might require you to put the IOIOLooper thing into an AsyncTask though, I have no experience with that board or its Java API.
Also you shouldn't need the "runOnUiThread", or the Runnable at all in your setText() method. That seems to be only being called from the main thread anyway.
What you'll want to do is override your Handlers handleMessage() to call setText(). Then inside the FrequencyThread make a call to handler.sendMessage() passing back the data and / or message(i.e. error).
I tried using the code you've posted to make an example but I am having trouble following it honestly.