滑块滑动&滑块点击&good、best、miss效果实现

 

android studio2D 游戏 android studio怎么做游戏_android studio

 

        实现思路大致是,设置滑块(继承自button)对象,两个轨道每个轨道都对应一个ArrayList<EachButton>滑块列表,每个滑块都定义了它的轨道位置、滑动动画、起始时间、滑动时间、滑动路径的属性。

       游戏页面得到来自模式选择页面的会话,传递过来了音频信息、模式信息,然后调用节奏点获取的HandleData类来获取音频的节奏时间点N个,初始化N个滑块,将‘节奏时间点-滑动时间’作为滑块的滑动动画延迟时间(这样滑到按钮处时就刚好到那个乐点了),将模式信息中的速度、滑块多少等来量化滑块移动时间、节奏点个数的信息,然后开始全部滑块的start方法来开启动画。

        点击下方的FREE按钮会触发方法,得到该按钮所在轨道的最近一个滑块的位置,与按钮的位置作比较给出good、best级别,将该滑块移出轨道队列;如果滑块动画结束仍没被点击(还在队列),则判断为miss,并移出队列。

 

滑块移动动画核心代码为:

Path path=new Path();
   path.moveTo(fromX,fromY);//设置path开始坐标
   path.lineTo(toX,toY);//设置path结束坐标
   animator=ObjectAnimator.ofFloat(this,
           Property.of(EachButton.class,Float.class,"translationX"),
           Property.of(EachButton.class,Float.class,"translationY"),
           path);//设置animator是沿着path路径这种方式移动的

   animator.setDuration(time);//设置动画时长,即整个移动过程的时间
   animator.setStartDelay(startTime);//设置动画延迟时间,到节奏点了再让滑块动
   LinearInterpolator lin = new LinearInterpolator();//匀速运动
   animator.setInterpolator(lin);

   animator.addListener(new Animator.AnimatorListener() {
       @Override
       public void onAnimationStart(Animator animation) {
           mlayout.addView(eachButton);
       }

       @Override
       public void onAnimationEnd(Animator animation) {
           mlayout.removeView(eachButton);
       }
   });

good、best、miss提示的弹出、淡化、消失的伪跳跃渐变效果的核心代码为:

View view = LayoutInflater.from(mcontext).inflate(R.layout.toast, null);//得到视图里的toast,更新toast的文本为miss
   TextView textView = view.findViewById(R.id.textView);
   textView.setText("miss");
   Toast toast = GameActivity.toast;//(设置一个toast变量,有初始样式,每次点击后更新它的TextView,并show()出来即可)
   toast.setView(view);//更新toast的样式
   toast.setDuration(Toast.LENGTH_SHORT);

   ObjectAnimator.ofArgb(textView, "textColor",
           Color.parseColor("#ff333333"),
           Color.parseColor("#00333333"))
           .setDuration(1000)
           .start();//设置toast的动画显示时间及颜色变化(灰色到灰色透明这种伪渐变效果)
   toast.show();//展示这个toast

 底部按钮点击时变色的核心代码为:

ObjectAnimator.ofArgb(bottoms[i], "backgroundColor",
           Color.parseColor("#F3E77C"),
           Color.parseColor("#F8E097"))
           .setDuration(500)
           .start();

 

toast.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/toast"
    >
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/textView"
        android:text=""
        android:textStyle="bold"
        android:textSize="40sp"
        android:shadowRadius="7.0"
        android:shadowDx="-7"
        android:shadowDy="7"
        android:shadowColor="@color/colorSilver"
        android:layout_marginBottom="125dp"
        android:layout_gravity="center_horizontal"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        ></TextView><!--这里是constraintlayout,与底部左边的距离依靠具体手机分辨率决定-->

</androidx.constraintlayout.widget.ConstraintLayout>

EachButton类

package com.example.free.Classes;


import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Path;
import android.util.Property;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.widget.AppCompatButton;
import androidx.constraintlayout.widget.ConstraintLayout;

import com.example.free.GameActivity;
import com.example.free.R;
import com.example.free.ResultActivity;

import java.util.Timer;
import java.util.TimerTask;


public class EachButton extends AppCompatButton {

    //这里的坐标位置都是用的像素px,因为实时获得滑块的运动位置得到返回值是像素
    int fromX=0;//滑块移动的起始点x坐标
    int fromY=0;//起始点y坐标
    int toX=0;//滑块移动的终止点x坐标
    int toY=0;//终止点y坐标
    int startTime=0;//开始移动的时间点
    int time=0;//滑动过程的时间长度

    int width=240;//px 滑块宽
    int height=120;//滑块长

    ConstraintLayout mlayout;//滑块所在的视图
    Context mcontext;//滑块所在的上下文活动

    public ObjectAnimator animator;//动画实例

    public boolean state=true;//按钮的状态,是否还存在于队列中

    int path=0;//所在轨道

   public EachButton(Context context,int _width,int _height,int fx,int fy,int tox,int toy,int st,int t,int p){
        super(context);

        this.fromX=fx;
        this.fromY=fy;
        this.toX=tox;
        this.toY=toy;
        this.startTime=st;
        this.time=t;
        this.path=p;
        width=_width;
        height=_height;

        mcontext=context;

        this.setBackgroundColor(0xaaF8E097);
        this.setWidth(width);
        this.setHeight(height);
        this.layout(1000,0,1000+width,height);//layout里的参数是滑块初始位置的四个顶点的坐标(左上x,左上y,右下x,右下y)

    }

    //通过设置滑块动画的延迟时间,比如20s时是一个节奏点,我们从一开始就初始化了滑块的动画,设置延迟时间为20s
    public void start(ConstraintLayout layout){

        mlayout=layout;
        Path path=new Path();
        path.moveTo(fromX,fromY);//设置path开始坐标
        path.lineTo(toX,toY);//设置path结束坐标
        animator=ObjectAnimator.ofFloat(this,
                Property.of(EachButton.class,Float.class,"translationX"),
                Property.of(EachButton.class,Float.class,"translationY"),
                path);//设置animator是沿着path路径这种方式移动的

        animator.setDuration(time);//设置动画时长,即整个移动过程的时间
        animator.setStartDelay(startTime);//设置动画延迟时间,到节奏点了再让滑块动

        LinearInterpolator lin = new LinearInterpolator();//匀速运动
        animator.setInterpolator(lin);

        final EachButton eachButton=this;//设置滑块动画监听动作,等到动画结束后"销毁"该滑块,方便进行下一次点击计算,否则点击动作识别的一直是第一个滑块
        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                mlayout.addView(eachButton);//
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                mlayout.removeView(eachButton);//动画结束移除该滑块

                //滑块的消除(移出buttons队列)由两种行为触发,一种是点击,一种是没点到过去了的miss状态,以下是第二种
                if(eachButton.state) {//eachButton还在队列里,但动画结束了,这意味着没点击成功
                    View view = LayoutInflater.from(mcontext).inflate(R.layout.toast, null);//得到视图里的toast,更新toast的文本为miss
                    TextView textView = view.findViewById(R.id.textView);
                    textView.setText("miss");
                    Toast toast = GameActivity.toast;

                    toast.setView(view);//更新toast的样式
                    toast.setDuration(Toast.LENGTH_SHORT);

                    ObjectAnimator.ofArgb(textView, "textColor",
                            Color.parseColor("#ff333333"),
                            Color.parseColor("#00333333"))
                            .setDuration(1000)
                            .start();//设置toast的动画显示时间及颜色变化(灰色到灰色透明这种伪渐变效果)
                    toast.show();//展示这个toast

                    GameActivity.buttons.get(eachButton.path).remove(0);//将button移出队列
                    GameActivity.missTimes+=1;//miss次数加一

                    
//如果最后一个滑块是由miss结尾的,则在这里结束队列并且跳转至成绩结算页面
if((GameActivity.buttons.get(0).size()+GameActivity.buttons.get(1).size()==0)&&GameActivity.activityState) {
                        final Intent iintent = new Intent(mcontext, ResultActivity.class);
                        iintent.putExtra("wholeScore",GameActivity.wholeScore);
                        iintent.putExtra("resultScore",GameActivity.resultScore);
                        iintent.putExtra("bestTimes",GameActivity.bestTimes);
                        iintent.putExtra("goodTimes",GameActivity.goodTimes);
                        iintent.putExtra("missTimes",GameActivity.missTimes);
                        TimerTask task = new TimerTask(){
                            public void run(){
                                mcontext.startActivity(iintent);
                            }
                        };
                        Timer timer = new Timer();
                        timer.schedule(task, 2000);
                    }
                }
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }
            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });

        animator.start();//开始动画

    }
}