I need to create a movement/changing position of the camera with the x,y axes by touching screen. I've read many of the previous questions, but nowhere noticed something that will solve my problem.
how can i use this code ? :
class ESSurfaceView extends GLSurfaceView {
private final float TOUCH_SCALE_FACTOR = 180.0f / 320;
private float mPreviousX;
private float mPreviousY;
#Override
public boolean onTouchEvent(MotionEvent e) {
float x = e.getX();
float y = e.getY();
switch (e.getAction()) {
case MotionEvent.ACTION_MOVE:
float dx = x - mPreviousX;
float dy = y - mPreviousY;
if (y > getHeight() / 2) {
dx = dx * -1 ;
}
if (x < getWidth() / 2) {
dy = dy * -1 ;
}
GLRenderer.setAngle(
GLRenderer.getAngle() +
((dx + dy) * TOUCH_SCALE_FACTOR));
requestRender();
}
mPreviousX = x;
mPreviousY = y;
return true;
}
public ESSurfaceView(Context context)
{
super(context);
setEGLContextClientVersion(2);
GLRenderer renderer = new GLRenderer();
setRenderer(renderer);
// Render the view only when there is a change in the drawing data
//setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}}
Offical Android Training has an example (mostly identically to yours) for that:
https://developer.android.com/training/graphics/opengl/touch.html
It also offers complete source.
By the way - you never move the camera in openGL - you move the world.
For further understanding please read:
http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/
This should give you a better understanding on how things work...
Related
I'm trying to make a 3D camera around a player on a game made with LWJGL3.
With the mouse scroll wheel you'll be able to zoom on the player.
How my function should be ?
How I query every event the mousewheel value.
glfwSetScrollCallback(window.getWindowHandle(), (windowHandle, xoffset, yoffset) -> {
currentMouseWheel.x = xoffset;
currentMouseWheel.y = yoffset;
});
My function to get Scroll delta based on the previous call :
(PS : input is called each gameLoop)
public void input(Window window) {
dMouseWheel.x = 0;
dMouseWheel.y = 0;
if (inWindow) {
double dx = currentMouseWheel.x - previousMouseWheel.x;
double dy = currentMouseWheel.y - previousMouseWheel.y;
if (dx != 0) {
dMouseWheel.x = (float) dx;
}
if (dy != 0) {
dMouseWheel.y = (float) dy;
}
}
previousMouseWheel.x = currentMouseWheel.x;
previousMouseWheel.y = currentMouseWheel.y;
}
xoffset and yoffset are never equals to 0.
I have to make big impulse on the scrollwheel to see the delta change.
What should I do ?
Can you give me an example ?
UPDATE : I found out !
How to do it :
First of all set 2 vectors variables :
private Vector2d currentMouseWheel;
private Vector2f dMouseWheel;
Then in your Scroll callback :
glfwSetScrollCallback(window.getWindowHandle(), (windowHandle, xoffset, yoffset) -> {
currentMouseWheel.x = xoffset;
currentMouseWheel.y = yoffset;
});
Then each game loop iteration, you call a function to update it
public void input() {
dMouseWheel.x = 0;
dMouseWheel.y = 0;
if (inWindow) {
dMouseWheel.x = (float) currentMouseWheel.x;
dMouseWheel.y = (float) currentMouseWheel.y;
}
currentMouseWheel.x = 0;
currentMouseWheel.y = 0;
}
Then you'll have dMouseWheel with the correct Scroll delta !
I have a CustomView and an Image view. The CustomView is a ball that moves around the screen and bounces off the walls. The Image is a quarter circle that you can rotate in a circle on touch. I am trying to make my game so that when the filled pixels from the CustomView cross paths with the Filled pixels from the ImageView a collision is detected. The problem that I am having is I do not know how to retrieve where the filled pixels are on each view.
Here is my XML code
<com.leytontaylor.bouncyballz.AnimatedView
android:id="#+id/anim_view"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
/>
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/quartCircle"
android:layout_gravity="center_horizontal"
android:src="#drawable/quartercircle"
android:scaleType="matrix"/>
My MainActivity
public class MainActivity extends AppCompatActivity {
private static Bitmap imageOriginal, imageScaled;
private static Matrix matrix;
private ImageView dialer;
private int dialerHeight, dialerWidth;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// load the image only once
if (imageOriginal == null) {
imageOriginal = BitmapFactory.decodeResource(getResources(), R.drawable.quartercircle);
}
// initialize the matrix only once
if (matrix == null) {
matrix = new Matrix();
} else {
// not needed, you can also post the matrix immediately to restore the old state
matrix.reset();
}
dialer = (ImageView) findViewById(R.id.quartCircle);
dialer.setOnTouchListener(new MyOnTouchListener());
dialer.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// method called more than once, but the values only need to be initialized one time
if (dialerHeight == 0 || dialerWidth == 0) {
dialerHeight = dialer.getHeight();
dialerWidth = dialer.getWidth();
// resize
Matrix resize = new Matrix();
resize.postScale((float) Math.min(dialerWidth, dialerHeight) / (float) imageOriginal.getWidth(), (float) Math.min(dialerWidth, dialerHeight) / (float) imageOriginal.getHeight());
imageScaled = Bitmap.createBitmap(imageOriginal, 0, 0, imageOriginal.getWidth(), imageOriginal.getHeight(), resize, false);
// translate to the image view's center
float translateX = dialerWidth / 2 - imageScaled.getWidth() / 2;
float translateY = dialerHeight / 2 - imageScaled.getHeight() / 2;
matrix.postTranslate(translateX, translateY);
dialer.setImageBitmap(imageScaled);
dialer.setImageMatrix(matrix);
}
}
});
}
MyOnTouchListener class:
private class MyOnTouchListener implements View.OnTouchListener {
private double startAngle;
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startAngle = getAngle(event.getX(), event.getY());
break;
case MotionEvent.ACTION_MOVE:
double currentAngle = getAngle(event.getX(), event.getY());
rotateDialer((float) (startAngle - currentAngle));
startAngle = currentAngle;
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
}
private double getAngle(double xTouch, double yTouch) {
double x = xTouch - (dialerWidth / 2d);
double y = dialerHeight - yTouch - (dialerHeight / 2d);
switch (getQuadrant(x, y)) {
case 1:
return Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;
case 2:
return 180 - Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;
case 3:
return 180 + (-1 * Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI);
case 4:
return 360 + Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;
default:
return 0;
}
}
/**
* #return The selected quadrant.
*/
private static int getQuadrant(double x, double y) {
if (x >= 0) {
return y >= 0 ? 1 : 4;
} else {
return y >= 0 ? 2 : 3;
}
}
/**
* Rotate the dialer.
*
* #param degrees The degrees, the dialer should get rotated.
*/
private void rotateDialer(float degrees) {
matrix.postRotate(degrees, dialerWidth / 2, dialerHeight / 2);
dialer.setImageMatrix(matrix);
}
And my AnimatedView
public class AnimatedView extends ImageView {
private Context mContext;
int x = -1;
int y = -1;
private int xVelocity = 10;
private int yVelocity = 5;
private Handler h;
private final int FRAME_RATE = 60;
public AnimatedView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
h = new Handler();
}
private Runnable r = new Runnable() {
#Override
public void run() {
invalidate();
}
};
protected void onDraw(Canvas c) {
BitmapDrawable ball = (BitmapDrawable) mContext.getResources().getDrawable(R.drawable.smallerball);
if (x<0 && y <0) {
x = this.getWidth()/2;
y = this.getHeight()/2;
} else {
x += xVelocity;
y += yVelocity;
if ((x > this.getWidth() - ball.getBitmap().getWidth()) || (x < 0)) {
xVelocity = xVelocity*-1;
}
if ((y > this.getHeight() - ball.getBitmap().getHeight()) || (y < 0)) {
yVelocity = yVelocity*-1;
}
}
c.drawBitmap(ball.getBitmap(), x, y, null);
h.postDelayed(r, FRAME_RATE);
}
public float getX() {
return x;
}
public float getY() {
return y;
}
}
My question is: How can I retrieve the filled pixels from both of these views, and pass them through a function that detects a collision.
Thanks in advance for the help!:)
You could encapsulate your images with Array of points marking your border coordinates(and update them on move/calculate them based on origin) then you'll be able to decide whether oposing object are in touch or not(if any of oposing arrays share the same point)
You really need to define "filled pixels". I assume you mean the non-transparent pixels. The easiest way to find those, is by converting your entire view into a bitmap and iterating through its pixels. You can convert a View into a Bitmap like this:
private Bitmap getBitmapFromView(View view) {
view.buildDrawingCache();
Bitmap returnedBitmap = Bitmap.createBitmap(view.measuredWidth,
view.measuredHeight,
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(returnedBitmap);
canvas.drawColor(Color.WHITE, PorterDuff.Mode.SRC_IN);
Drawable drawable = view.background;
drawable.draw(canvas);
view.draw(canvas);
return returnedBitmap;
}
You'd also need to get the absolute location of the views:
Point getViewLocationOnScreen(View v) {
int[] coor = new int[]{0, 0};
v.getLocationOnScreen(coor);
return new Point(coor[0], coor[1]);
}
Then you just have to iterate through the pixels of each Bitmap, check their colors to know whether they're "filled", and also check whether they're overlapping based on their coordination inside the bitmaps and the absolute location of the views on screen.
Iterating through a bitmap's is done like this:
for (int x = 0; x < myBitmap.getWidth(); x++) {
for (int y = 0; y < myBitmap.getHeight(); y++) {
int color = myBitmap.getPixel(x, y);
}
}
But I must say, I don't think performing this sort of heavy computations on UI thread is really a good idea. There are dozens of much better ways to detect collisions than pixel-perfect checking. This will probably come out extremely laggy.
I programmed a drawing application, I want to retrieve all the X Y of my drawing. That is to say each time I touch the screen, the coordinates x and y I put them in a two dimensional table ,
I made a toast to find out when the coordinates change, and I found that they change in the movetouch method, so I declare a table in the method and I still make a toast to see the 10 line Of my array, the toast changed co-ordination so I understood that in fact values are crushed whenever the x and y change, or I am planting
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startTouch(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
upTouch();
invalidate();
break;
case MotionEvent.ACTION_MOVE:
moveTouche(x, y);
invalidate();
break;
}
return true;
}
Method moveTouch
public void moveTouche (float x,float y ) {
if ((canDraw)&& drawing) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if(dx >= Tolerance || dy >= Tolerance){
path.quadTo(mX,mY,(x+mX)/2,(y+mY)/2);
mX = x ;
mY = y;
double[][] point = new double [99][2];
for (int i = 0; i < 99; i++) {
point[i][0]=x;
point[i][1]=y;
}
Toast.makeText(getContext(),"y = "+point[10][1]+" ",Toast.LENGTH_LONG).show();
}}
}
You can read as many points as you want from any path. Example how to read coordinates from the middle of path:
PathMeasure pm = new PathMeasure(myPath, false);
//coordinates will be here
float aCoordinates[] = {0f, 0f};
//get coordinates of the middle point
pm.getPosTan(pm.getLength() * 0.5f, aCoordinates, null);
You can pass any distance from the path start to get point coordinates.
I have been really stuck on this for the past day or so and I could really use somebody's input as to where I'm going wrong
So I have a 'Play Game' image, I'm loading it into my game as follows:
playGame = new SimpleControl(250.0f, 225.0f, 140.0f, 30.0f, "PlayBtn", this);
The SimpleControl object looks like this:
public SimpleControl(float x, float y, float width, float height,
String bitmapName, GameScreen gameScreen) {
super(x, y, width, height, gameScreen.getGame().getAssetManager()
.getBitmap(bitmapName), gameScreen);
public boolean isActivated() {
// Consider any touch events occurring in this update
Input input = mGameScreen.getGame().getInput();
// Check if any of the touch events were on this control
BoundingBox bound = getBound();
for (int idx = 0; idx < TouchHandler.MAX_TOUCHPOINTS; idx++) {
if (input.existsTouch(idx)) {
if (bound.contains(input.getTouchX(idx), input.getTouchY(idx))) {
return true; }}}
return false; }
I have an update method in my Game class which goes like this:
Input input = mGame.getInput();
List<TouchEvent> touchEvents = input.getTouchEvents();
if (touchEvents.size() > 0) {
TouchEvent touchevent = touchEvents.get(0);
{
if (playGame.isActivated()) {
mGame.getScreenManager().removeScreen(this.getName());
AboutScreen aboutScreen = new AboutScreen(mGame);
mGame.getScreenManager().addScreen(aboutScreen);
}
Unfortunately when I'm running this, the button does not pick up any touch event but there's a little section of the screen that does:
http://i.imgur.com/R4nVIRP.png
BoundingBox.java:
public BoundingBox(float x, float y, float halfWidth, float halfHeight) {
this.x = x;
this.y = y;
this.halfWidth = halfWidth;
this.halfHeight = halfHeight;
}
public boolean contains(float x, float y) {
return (this.x - this.halfWidth < x && this.x + this.halfWidth > x
&& this.y - this.halfHeight < y && this.y + this.halfHeight > y);
}
Thank you in advance :)
Your contains(float x, float y) looks incorrect.
this.x - this.halfwidth < x means it checks for x at a location before the start of the bounding box.
For example your x should be -
(x>=this.x && x<=(this.x + 2*this.halfwidth))
And y should be
(y>=this.y && y<=(this.y + 2.this.halfHeight))
(Assuming halfWidth and halfHeight are what they say)
(also, can't open the imgur link since its blocked for us)
I've downloaded and run the opengl sample from android. now, i try to make a line with opengl es 2.0 while user is moving their finger. so far this is what i've done:
on my GlSurfaceView class onTouchEvent method:
#Override
public boolean onTouchEvent(MotionEvent e) {
mScaleDetector.onTouchEvent(e);
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN: {
float x = e.getX();
float y = e.getY();
mPreviousX = x;
mPreviousY = y;
// Save the ID of this pointer
mActivePointerId = e.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = e.findPointerIndex(mActivePointerId);
final float x = e.getX(pointerIndex);
final float y = e.getY(pointerIndex);
if (!mScaleDetector.isInProgress()) {
mRenderer.setLineCoordinates(mPreviousX, mPreviousY, x, y);
requestRender();
}
mPreviousX = x;
mPreviousY = y;
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (e.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = e.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mPreviousX = e.getX(newPointerIndex);
mPreviousY = e.getY(newPointerIndex);
mActivePointerId = e.getPointerId(newPointerIndex);
}
break;
}
}
return true;
}
on my renderer class:
public void onDrawFrame(GL10 gl) {
// Draw background color
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// Set GL_MODELVIEW transformation mode
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity(); // reset the matrix to its default state
// When using GL_MODELVIEW, you must set the view point
GLU.gluLookAt(gl, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
gl.glScalef(factor, factor, 0.0f);
// Draw triangle
mTriangle.draw(gl);
mLine.draw(gl);
}
function inside renderer class to change coordinates:
public void setLineCoordinates(float mPreviousX
, float mPreviousY, float x, float y) {
// TODO Auto-generated method stub
lineCoords[0] = (float) (mPreviousX * 2.0 / WIDTH - 1.0);
lineCoords[1] = (float) (mPreviousY * -2.0 / HEIGHT + 1.0);
lineCoords[2] = 0.0f;
lineCoords[3] = (float) (x * 2.0 / WIDTH - 1.0);
lineCoords[4] = (float) (y * -2.0 / HEIGHT + 1.0);
lineCoords[5] = 0.0f;
}
when i move my finger, there's no line, just a moving piece of tiny line...
how can i draw the whole line and keep the line drawing while i keep moving my finger? also start new line when i touch down the screen again.
UPDATED
Thanks for #Gil Moshayof i can draw line now with my version like this
public void setLineCoordinates(float mPreviousX
, float mPreviousY, float x, float y) {
// TODO Auto-generated method stub
float lineCoords[] = new float[6];
lineCoords[0] = (float) (mPreviousX * 2.0 / WIDTH - 1.0);
lineCoords[1] = (float) (mPreviousY * -2.0 / HEIGHT + 1.0);
lineCoords[2] = 0.0f;
lineCoords[3] = (float) (x * 2.0 / WIDTH - 1.0);
lineCoords[4] = (float) (y * -2.0 / HEIGHT + 1.0);
lineCoords[5] = 0.0f;
bufferOfArrays.add(new Line(lineCoords));
if(isFirst) {
isFirst = false;
listOfArrays.addAll(bufferOfArrays);
view.requestRender();
}
}
public void setFirst(boolean isFirst) {
this.isFirst = isFirst;
}
and then:
#Override
public void onDrawFrame(GL10 gl) {
//... color, matrix, look at etc..
// Draw triangle
//mTriangle.draw(gl);
//TODO mLine.draw(gl);
for(Line line : listOfArrays) {
line.draw(gl);
}
isFirst = true;
}
but my app starts to slow down while moving my finger, still have no idea what's happening
The problem here is that you're always setting mPreviousX & mPreviousY on every move. Actually, the problem starts with the name you chose for these parameters. A better name would be mLineStartX/Y.
Set mLineStartX/Y on the touch down event, then update the x/y on every move and don't change the mLineStartX/Y.
As for adding multiple lines, you'll need to implement a bit more logic. You'll need to maintain an array list of some class you define which has 2 point (2xPointFs). The moment the user lifts his finger (touch up / touch cancel), create a new class with the 2 points, and on your render method, make sure you go over all such lines and render them.