Android Game Development: Surface View Multi-touch Perfect Piano Game Demo

First, when we use Surface View to develop small games, if we need to draw buttons and interactive objects on the form, then we need to monitor the multi-touch screen, and every touch change needs to interact with the game. How to achieve this?

CSDN blog @MXout has a detailed introduction: Multi-touch Android Custom View There is a very detailed introduction, let's learn together, the main points of knowledge are as follows:

A review of the basic knowledge of multi-touch technology in Android:

For single touch, MotionEvent's type constants and meanings are as follows:

MotionEvent.ACTION_DOWN://Touch Point Press

MotionEvent.ACTION_MOVE://Touch Point Movement

MotionEvent.ACTION_UP://Touch Point Lift

For multi-touch, MotionEvent's type constants and meanings are as follows:

MotionEvent.ACTION_DOWN://(Master Control Point) First Touch Point Press

MotionEvent.ACTION_POINTER_DOWN://(Auxiliary Control Point) After the first touch point press

MotionEvent.ACTION_MOVE://Master and Auxiliary Control Point Movement

MotionEvent.ACTION_UP://Last touch point raised

MotionEvent.ACTION_POINTER_UP://Non-last touch point raised

The contact id of this touch event can be obtained by operation and shift operation for the event of multi-touch and the event of pressing or lifting the main/auxiliary control points:

int id = (event.getAction()&MotionEvent.ACTION_POINTER_ID_MASK) >>> MotionEvent.ACTION_POINTER_ID_SHIFT;

 

Secondly, after learning, I realized a perfect piano game Demo, and tested the realization method of multi-touch screen. If you have better implementation, please give more advice.

Following the usual practice, the picture above is as follows:

(There are seven red keys on the screen. You can press one key alone or multiple keys at the same time. When you press a key, the color turns grey.)

3. Perfect Piano Game Source

(1) OnTouchPoint Touch Point Class

Record the ID, screen X, Y coordinates of the touch point pressed

package com.hawkonline.touchscreendome;

public class OnTouchPoint {

    int id;//Touch point id
    float x;//Touch point x coordinates
    float y;//Touch point y coordinate

    public OnTouchPoint(int id, float x, float y) {
        this.id = id;
        this.x = x;
        this.y = y;
    }

    public void setLocation(float x, float y) {
        this.x = x;
        this.y = y;
    }

    public float getX() {
        return x;
    }

    public float getY() {
        return y;
    }
}

(2) NoteKeys note keys

The button (red) is displayed on the interface, and the color turns gray when the button is pressed. And the onClick interface is implemented, which can be used to press the corresponding event when the note key is pressed.

package com.hawkonline.touchscreendome;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;

import java.util.ArrayList;

//Note keys
public class NoteKeys{
    private UClickListener uClickListener;
    private float drawX,drawY,width,height;
    private RectF rectF;
    private Paint mPaint;// Paint brush
    private boolean isOnTouch=false;

    public NoteKeys(float drawX,float drawY,float width,float height){
       this.drawX=drawX+5;
       this.drawY=drawY+5;
       this.width=width-10;
       this.height=height-10;

        rectF=new RectF(drawX,drawY,drawX+width,drawY+height);

        mPaint = new Paint();
        mPaint.setAntiAlias(true);// Anti-aliased
        mPaint.setStrokeWidth(5);//Set the thickness of brush paint1
        mPaint.setStyle(Paint.Style.FILL);//Setting the Style of Brush Paint 1

    }

    public void drawSelf(Canvas canvas) {

        // Draw the circle outside the button
        if (isOnTouch){
            mPaint.setColor(Color.GRAY);
        }else{
            mPaint.setColor(Color.RED);
        }

        canvas.drawRect(rectF,mPaint);

    }
    public void setuClickListener(UClickListener uClickListener){
        this.uClickListener=uClickListener;
    }

    public boolean onTouchUView(ArrayList<OnTouchPoint> pointArrayList) {

        for( int i=0;i<pointArrayList.size();i++){
            if(rectF.contains(pointArrayList.get(i).getX(),pointArrayList.get(i).getY())){

               uClickListener.onClick();
                isOnTouch=true;
                return true;
            }
        }
        isOnTouch=false;
        return false;
    }

    interface UClickListener {
        void onClick();
    }
}

(3) MySurfaceView Rewriting SurfaceView to Realize Game Home Course

package com.hawkonline.touchscreendome;

import android.graphics.Canvas;
import android.graphics.Color;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import java.util.ArrayList;

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {

    private ArrayList<OnTouchPoint> pointArrayList  = new ArrayList<>();//Touch Point List

    private NoteKeys [] noteKeys;


    public  MySurfaceView(MainActivity activity){
        super(activity);

        //this.activity = activity;
        this.getHolder().addCallback(this);
        noteKeys=new NoteKeys[7];

        for(int i=0;i<7;i++){
            noteKeys[i]=new NoteKeys(MainActivity.screenWidth/7*i,MainActivity.screenHeight/2,MainActivity.screenWidth/7,MainActivity.screenHeight/2-20);
            noteKeys[i].setuClickListener(new NoteKeys.UClickListener() {
                @Override
                public void onClick() {
                    //override method
                }
            });
        }


    }

    public void onDraw(Canvas canvas){
        canvas.drawColor(Color.BLACK);

        for(NoteKeys k:noteKeys){
          k.drawSelf(canvas);
        }

    }



    public void repaint(){
        SurfaceHolder holder = this.getHolder();
        Canvas canvas = holder.lockCanvas();
        try {
            synchronized (holder){
                onDraw(canvas);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(canvas!=null){
                holder.unlockCanvasAndPost(canvas);
            }
        }
    }

    public boolean onTouchEvent(MotionEvent event){
        int actionMasked = event.getActionMasked();//Obtaining Multi-touch Detection Points
        int id = (event.getAction()& MotionEvent.ACTION_POINTER_ID_MASK) >>> MotionEvent.ACTION_POINTER_ID_SHIFT;//Unsigned right-shift to get touch point id

        switch (actionMasked){
            case MotionEvent.ACTION_DOWN://First touch point press
                pointArrayList.add(id,new OnTouchPoint(id,event.getX(id), event.getY(id)));//Insertion sorted by id index size
                //Sequence corresponds to the order in which event.getX(i) takes coordinates when ACTION_MOVE
                break;
            case MotionEvent.ACTION_POINTER_DOWN://After the first touch point press
                pointArrayList.add(id,new OnTouchPoint(id,event.getX(id),event.getY(id)));//When the touch point is pressed, it will get (0~Number-1)
                //The smallest id number available, i.e. the point that is subsequently pressed, is available
                //id of touch point that has been raised previously
                break;
            case MotionEvent.ACTION_MOVE://Main and auxiliary point movement
                for (int i = 0;i<pointArrayList.size();i++){
                    try {
                        //float x =event.getX(pointArrayList.get(i).id); // The subscript may cross the boundary, because the ID of the touch point is not necessarily equal to its subscript.
                        float x = event.getX(i);
                        float y = event.getY(i);
                        pointArrayList.get(i).setLocation(x,y);
                    }catch (Exception e){
                        Log.d("MySurfaceView", "onTouchEvent: point.id=" + pointArrayList.get(i).id);
                        e.printStackTrace();
                    }
                }
                //PointArrayList. get (id). setLocation (event. getX (id), event. getY (id); //id = 0, that is, if there is no loop traversal, only the master control point will be updated.
                break;
            case MotionEvent.ACTION_UP://Last point raised
                pointArrayList.clear();
                break;
            case MotionEvent.ACTION_POINTER_UP://Not the last point to lift
                pointArrayList.remove(id);//After deleting a touch point, the point after the touch point moves forward, so that the id of the point does not necessarily equal the subscript.
                break;
        }
        for(NoteKeys k:noteKeys){
            k.onTouchUView(pointArrayList);
        }
        repaint();
        return true;
    }

    public void surfaceCreated(SurfaceHolder holder){

    }

    public void surfaceChanged(SurfaceHolder holder,int arg1,int arg2,int arg3){
        this.repaint();
    }

    public void surfaceDestroyed(SurfaceHolder holder){

    }
    
}

 

(4) Add the following code to the onCreate event in MainActivity:

public class MainActivity extends AppCompatActivity {
    static float screenHeight;
    static float screenWidth;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

       
        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);

        screenHeight = metrics.heightPixels;//Screen size
        screenWidth = metrics.widthPixels;

        MySurfaceView mySurfaceView = new MySurfaceView(this);
        setContentView(mySurfaceView);
    }
}

(5) In this way, we have achieved a perfect piano Demo, which is one of my solutions. I don't know if it is right or not, look for advice!!

Tags: Android SurfaceView Java

Posted on Mon, 09 Sep 2019 03:06:07 -0700 by fareedreg