im trying to make a simple game with Gles on android.
i've googled a lot for a solution but i couldnt find anyone mentioning how to implement ontouchevent in GLES.
here i how i implemented it so far... i know it's a silly way to do it :D but that was only way i could come up to set positionx variable free while there is no tapping
a brief summary of my Renderer Class...
public class GLmain implements Renderer {
public float mX,mY;
public boolean clicked;
public GLmain()
{
clicked=false;
}
public void onDrawFrame(GL10 gl) {
if(clicked)
{
positionx=mX;
positiony=mY;
clicked=false
}
}
}
in and Activity class
public class Alpha extends Activity {
private GLSurfaceView glSurface;
public float mousex,mousey;
public GLmain glrend=new GLmain();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
glSurface = new GLSurfaceView(this);
glSurface.setRenderer(glrend);
setContentView(glSurface);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
glrend.clicked=true;
glrend.mX=event.getX();
glrend.mY=event.getY();
return true;
}}
It's not a silly way to do it! You can't call directly into the Renderer because it's on a different thread so you need to have the Renderer poll for the information when it needs it.
Related
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.
I'm creating a game with the Android NDK and OpenGLES (and not using NativeActivity / native_app_glue).
When the user rotates their phone, I don't want the whole activity to be destroyed and restarted, so I've added the necessary part to the manifest file:
android:configChanges="keyboardHidden|keyboard|orientation|screenSize"
and get an onConfigurationChanged() callback as expected. I also get a surfaceRedrawNeeded() callback, which is fine.
However, I would also expect to get a surfaceChanged() callback, but this never happens.
The java code looks like this:
public class MyActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
surface = new MySurface();
getWindow().takeSurface(surface);
view = new MyView(this);
getWindow().setContentView(view);
... native setup
}
...
#Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
...
}
...
}
class MyView extends View
{
public MyView(Context context)
{
super(context);
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) { ... }
... other input stuff
}
class MySurface implements SurfaceHolder.Callback2
{
#Override
public void surfaceCreated(SurfaceHolder holder) { ... }
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { ... }
#Override
public void surfaceRedrawNeeded(SurfaceHolder holder) { ... }
#Override
public void surfaceDestroyed(SurfaceHolder holder) { ... }
...
}
Questions:
Should I be getting a surfaceChanged() callback in the surface when the orientation changes?
If not, I guess I have two options:
Rotate my camera and adjust the viewport width / height (not really something I want to keep track of).
Call ANativeWindow_setBuffersGeometry (would this actually give me a rotated display?) and / or eglCreateWindowSurface again? Would I then need to recreate my EGLContext (and reload all my OpenGL stuff... ick)?
So in general, what's the "correct" way of handling orientation changes with regard to the ANativeWindow, EGLSurface and EGLContext?
I am quite new to Android and OpenGL ES. I have to create a GUI in OpenGL and I would like to use it as a Fragment in the main activity. In order to learn how to do this, I tried 2 tutorials - this Fragment tutorial and the Android developer tutorial on OpenGL ES.
But still I don't understand how exactly do I include an OpenGL view in a Fragment. OpenGL doesn't use XML layout files so this process is quite confusing for me. I would like to do something like this: inside the main activity from the Fragment tutorial I want to include a third Fragment with OpenGL. Go easy on me I am a beginner :)
If the developer tutorial is anything to go by, then the following setup would work:
Activity:
public class MainActivity extends FragmentActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener()
{
public void onBackStackChanged()
{
int backCount = getSupportFragmentManager().getBackStackEntryCount();
if (backCount == 0)
{
finish();
}
}
});
if (savedInstanceState == null)
{
getSupportFragmentManager().beginTransaction().add(R.id.main_container, new OpenGLFragment()).addToBackStack(null).commit();
}
}
}
Activity XML (activity_main.xml):
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/main_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Fragment:
public class OpenGLFragment extends Fragment
{
private GLSurfaceView mGLView;
public OpenGLFragment()
{
super();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
mGLView = new MyGLSurfaceView(this.getActivity()); //I believe you may also use getActivity().getApplicationContext();
return mGLView;
}
}
And I guess you need to make your own GLSurfaceView as the tutorial says:
class MyGLSurfaceView extends GLSurfaceView {
public MyGLSurfaceView(Context context){
super(context);
setEGLContextClientVersion(2);
// Set the Renderer for drawing on the GLSurfaceView
setRenderer(new MyRenderer());
}
}
And as the tutorial says, make your renderer:
public class MyGLRenderer implements GLSurfaceView.Renderer {
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
// Set the background frame color
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}
public void onDrawFrame(GL10 unused) {
// Redraw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
}
}
I'm using the Google Maps Android API v2 and I want to be able to open as fullscreen a small map (= SupportMapFragment in my view) on tap (or double tap).
Is this technically possible? If yes, how to achieve it?
Thanks in advance.
Yes, that is definitely possible.
You could for example have a Button, and upon pressing the Button the MapFragment / SupportMapFragment will be added to a container layout inside your Activity's layout file.
Inside the onClick method of your Button you add the Fragment to the container layout:
YourMapFragment f = new YourMapFragment();
getFragmentManager().beginTransaction().add(R.id.container_layout, f).commit();
In this case, I would recommend that "container_layout" is an empty FrameLayout, used as a placeholder in your Activity's layout file. This is where the Fragment will then appear.
If you really want to use Taps, this is how you can recognize for example a double-tap:
You will need an interface that the class that needs to recognize the
gesture has to implement
You need a custom TouchManager, that will use callbacks to the interface to interpret the gesture
The interface:
public interface GestureInterface {
/**
* returns the recognized gesture from the touchmanager
* and enables the user of the interface to react to the gesture (or not)
* #param gesture e.g. TouchManager.SWIPE_LEFT
*/
public void onGestureRecognized(int gesture);
}
The TouchManager:
public class TouchManager extends GestureDetector.SimpleOnGestureListener {
public static final int DOWN = 1;
public static final int DOUBLE_TAP = 2;
/** the class that initialized the gesture-recognizer and will be notified upon gestures made by the user */
private GestureInterface caller;
/**
* constructor
* #param the caller that implements the gestureinterface
*/
public TouchManager(GestureInterface caller) {
this.caller = caller;
}
/**
* you need this shit to return true, otherwise gestures wont work
*/
#Override
public boolean onDown(MotionEvent e) {
caller.onGestureRecognized(DOWN);
return true;
}
#Override
public boolean onDoubleTap(MotionEvent e) {
caller.onGestureRecognized(DOUBLE_TAP); // callback
return true;
}
}
And inside your Activity (or Customview or wherever you want to recognize the gesture): (In this case tapping on the Activity will call the Touchmanager.
public class YourActivity extends Activity implements GestureInterface {
private GestureDetector gd;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.yourlayout);
// initialize the touch manager
gd = new GestureDetector(this, new TouchManager(this));
}
#Override
public boolean onTouchEvent(MotionEvent event) {
return gd.onTouchEvent(event);
}
#Override
public void onGestureRecognized(int gesture) {
// react to callbacks
switch (gesture) {
case TouchManager.DOUBLE_TAP:
// do something
YourMapFragment f = new YourMapFragment();
getFragmentManager().beginTransaction().add(R.id.container_layout, f).commit();
break;
}
}
}
Wherever you want to recognize the gesture, you return GestureDetector.onTouchEvent(...).
Please tell me if this approach is safe or what i can use instead of a plain Thread to safely dispatch Touch Events to my activity, for the purpose of testing the entire flow witch triggers from that onTouch() method.
//this is the thread which fires Touch Events to my main activity
public class Monkey extends Thread {
Run r;
float x,y;
public Monkey (Run a)
{
r = a;
}
public void run()
{
int i = 0;
while(i<10000)
{
r.onTouch(r.geView(),
MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), 2, x++, y++, 0));
}
}
}
// and this is the main activity
public class Run extends Activity implements OnTouchListener{
private GLSurfaceView glSurface;
public View geView()
{
return glSurface;
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
glSurface = new GLSurfaceView(this);
glSurface.setOnTouchListener(this);
setContentView(glSurface);
m = new Monkey(this);
}
public boolean onTouch(View v, MotionEvent event)
{
//if from here i make other calls to classes that update my glsurface will my application eventualy crash?
}
}
Make use of singleton class
Singleton architecture