利用属性动画,打造一个重力弹跳的效果

效果图1
图片描述
效果图2
图片描述

一、属性动画:

在没有了解属性动画时,我们做动画一般用的就是View Animation,这样能简单实现位移、旋转、缩放以及alpha渐变等等效果,但是当我们用久了以后,总是会发现一些缺陷,例如:一些复杂动画无法实现;控件不会停留在动画结束位置等等。这个时候我们就需要了解属性动画了。

属性动画我是在之前郭神的一篇文章(点我查看)中所了解到的,在此就不一一赘述了。未了解属性动画的小伙伴可以去看看。

二、实现思路:

我们就可以开始动工了。做复杂的属性动画,我们要从Evarlutor入手:

    public class GravityEvarlutor implements TypeEvaluator<Integer> {

        @Override
        public Float evaluate(float fraction, Float startValue, Float endValue) {

        }
    }

接下来我们分析下运动流程。从物理课本中我们知道,物体掉落时受重力加速度g的影响而加速掉落,当接触地面时到达最大速度vmax然后回弹,回弹到达最大高度时速度为0。由于动能损失,每次回弹的高度会越来越小,直到最后静止。已知值确定:由于是动画过程,所以总时间t我们也应该知道;起始下落高度_h1,动能损失过程复杂,为了简单实现效果,所以我们做如下规定:弹跳次数为3次,回弹高度我们确定为_h2=_h1/7,_h3=_h1/35,_h4=_h1/105。如图所示:

图片描述

由于fraction的范围在0~1之间,因此我们可以把总时长看为1,接着我们可以得出如下代码:

public class GravityEvarlutor implements TypeEvaluator<Integer> {

    @Override
    public Float evaluate(float fraction, Integer startValue, Integer endValue) {
    //_h1为初始下落高度,_h2,_h3,_h4为各次的回弹高度
        int _h1 = endValue - startValue;
        int _h2 = _h1/7;
        int _h3 = _h1/35;
        int _h4 = _h1/105;

        //根据t = Math.sqrt(2 * h/a)以及t1 + 2*t2 + 2*t3 + 2*t4 = 1,可算出:
        double t1 = 1 / 2.28917;

        //从而得出重力加速度
        double a = (2 * _h1) / (t1 * t1);

        //再求出t2,t3,t4
        double t2 = Math.sqrt(2 * _h2 / a);
        double t3 = Math.sqrt(2 * _h3 / a);
        double t4 = Math.sqrt(2 * _h4 / a);

        //算出各个时间段的最大速度vt,因为每次在最大高度时满足v = 0,所以在接触地面时,达到最大速度vt = a * t
        double vt1 = a * t1;
        double vt2 = a * t2;
        double vt3 = a * t3;
        double vt4 = a * t4;
    }
}

接下来就是分析各个阶段的运动分析。下落时,小球的起始高度为h0,某个时刻的高度为h,根据公式s=(gt^2)/2,算出小球在时刻t所掉落的距离s,因此可以得出某时刻的小球高度h=h0-s=h0-(gt^2)/2。回弹时,小球的起始速度为vt,由于此刻是减速运动,所以某时刻t小球的高度h=vt*t-(gt^2)/2。由上面的运动分析图中看出,小球运动大致分7个阶段,我们拆解为1-1、2-1、2-2、3-1、3-2、4-1、4-2,ok,分析完毕,我们可以开始动代码了。

首先,我们对fraction进行拆解,进行分段分析:

    //将fraction进行分段,便于每段独立分析
        double fraction2_1 = fraction - t1;
        double fraction2_2 = fraction - t1 - t2;
        double fraction3_1 = fraction - t1 - (2 * t2);
        double fraction3_2 = fraction - t1 - (2 * t2) - t3;
        double fraction4_1 = fraction - t1 - (2 * t2) - (2 * t3);
        double fraction4_2 = fraction - t1 - (2 * t2) - (2 * t3) - t4;

接着,我们就可以算出各个时间段所对应的h值:

    //分段算出最终值h
        int h = 0;
        if(fraction <= t1) {
            h = (int)(_h1 - a * 0.5 * fraction * fraction);
        }else if(fraction > t1 && fraction <= (t1 + t2)){//2-1
            h = (int)(vt2 * fraction2_1 - a * 0.5 * fraction2_1 * fraction2_1);
        }else if(fraction > (t1 + t2) && fraction <= (t1 + (2 * t2))){//2-2
            h = (int)(_h2 - a * 0.5 * fraction2_2 * fraction2_2);
        }else if(fraction > (t1 + (2 * t2)) && fraction <= (t1 + (2 * t2) + t3)){//3-1
            h = (int)(vt3 * fraction3_1 - a * 0.5 * fraction3_1 * fraction3_1);
        }else if(fraction > (t1 + (2 * t2) + t3) && fraction <= (t1 + (2 * t2) + (2 * t3))){//3-2
            h = (int)(_h3 - a * 0.5 * fraction3_2 * fraction3_2);
        }else if(fraction > (t1 + (2 * t2) + (2 * t3))&& fraction <= (t1 + (2 * t2) + (2 * t3) + t4)){//4-1
            h = (int)(vt4 * fraction4_1 - a * 0.5 * fraction4_1 * fraction4_1);
        }else{//4-2
            h = (int)(_h4 - a * 0.5 * fraction4_2 * fraction4_2);
        }

ok,大功告成,但是有个小问题,因为我们算出来的值为double类型,强转成int后会有偏差,因此最后我们需要做下偏差矫正:

    if(fraction == 1){
            h = 0;
    }

最难的Evalutor到此就告一段落了,贴下总代码:

public class GravityEvarlutor implements TypeEvaluator<Integer> {

    /**
     * 因为fraction是从0~1,因此我们可以将总时长看成1
     * @param fraction
     * @param startValue
     * @param endValue
     * @return
     */
    @Override
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {

        //_h1为初始下落高度,_h2,_h3,_h4为各次的回弹高度
        int _h1 = endValue - startValue;
        int _h2 = _h1/7;
        int _h3 = _h1/35;
        int _h4 = _h1/105;

        //根据t = Math.sqrt(2 * h/a)以及t1 + 2*t2 + 2*t3 + 2*t4 = 1,可算出:
        double t1 = 1 / 2.28917;

        //从而得出重力加速度
        double a = (2 * _h1) / (t1 * t1);

        //再求出t2,t3,t4
        double t2 = Math.sqrt(2 * _h2 / a);
        double t3 = Math.sqrt(2 * _h3 / a);
        double t4 = Math.sqrt(2 * _h4 / a);

        //算出各个时间段的最大速度vt,因为每次在最大高度时满足v = 0,所以在接触地面时,达到最大速度vt = a * t
        double vt1 = a * t1;
        double vt2 = a * t2;
        double vt3 = a * t3;
        double vt4 = a * t4;

        //将fraction进行分段,便于每段独立分析
        double fraction2_1 = fraction - t1;
        double fraction2_2 = fraction - t1 - t2;
        double fraction3_1 = fraction - t1 - (2 * t2);
        double fraction3_2 = fraction - t1 - (2 * t2) - t3;
        double fraction4_1 = fraction - t1 - (2 * t2) - (2 * t3);
        double fraction4_2 = fraction - t1 - (2 * t2) - (2 * t3) - t4;

        //分段算出最终值h
        int h = 0;
        if(fraction <= t1) {
            h = (int)(_h1 - a * 0.5 * fraction * fraction);
        }else if(fraction > t1 && fraction <= (t1 + t2)){//2-1
            h = (int)(vt2 * fraction2_1 - a * 0.5 * fraction2_1 * fraction2_1);
        }else if(fraction > (t1 + t2) && fraction <= (t1 + (2 * t2))){//2-2
            h = (int)(_h2 - a * 0.5 * fraction2_2 * fraction2_2);
        }else if(fraction > (t1 + (2 * t2)) && fraction <= (t1 + (2 * t2) + t3)){//3-1
            h = (int)(vt3 * fraction3_1 - a * 0.5 * fraction3_1 * fraction3_1);
        }else if(fraction > (t1 + (2 * t2) + t3) && fraction <= (t1 + (2 * t2) + (2 * t3))){//3-2
            h = (int)(_h3 - a * 0.5 * fraction3_2 * fraction3_2);
        }else if(fraction > (t1 + (2 * t2) + (2 * t3))&& fraction <= (t1 + (2 * t2) + (2 * t3) + t4)){//4-1
            h = (int)(vt4 * fraction4_1 - a * 0.5 * fraction4_1 * fraction4_1);
        }else{//4-2
            h = (int)(_h4 - a * 0.5 * fraction4_2 * fraction4_2);
        }
        if(fraction == 1){
            h = 0;
        }
        return h;
    }
}

接下来我们只要运用到属性动画中即可。布局简单,我就不贴了,需要最后留意的一点是,我们的Evalutor是以y轴正方向为正值的场景来写的,而在屏幕坐标中是相反的,这时候我们需要加个负号,然而加了负号之后,发现还有一个高度为startValue-endValue的偏差值,此时我们进行相应处理就好。

public void play(View v){
        ValueAnimator va = ValueAnimator.ofObject(new GravityEvarlutor(), 0, layout_main.getHeight() - btn_play.getHeight() - ball.getHeight());
        va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                ball.setY(layout_main.getHeight() - btn_play.getHeight() - ball.getHeight() - (Integer) animation.getAnimatedValue());
            }
        });
        va.setDuration(800);
        va.setInterpolator(new LinearInterpolator());
        va.start();
}

至此,大功告成。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,以下是一个简单的重力计算界面示例,使用 Java 的 Swing 框架实现: ```java import javax.swing.*; import java.awt.*; import java.awt.event.*; public class GravityCalculator { private JFrame frame; private JPanel panel; private JLabel label_mass1, label_mass2, label_distance, label_result; private JTextField field_mass1, field_mass2, field_distance; private JButton button_calculate; public GravityCalculator() { createGUI(); bindEvents(); } private void createGUI() { frame = new JFrame("Gravity Calculator"); panel = new JPanel(new GridLayout(4, 2, 5, 5)); label_mass1 = new JLabel("Mass 1 (kg):"); panel.add(label_mass1); field_mass1 = new JTextField(); panel.add(field_mass1); label_mass2 = new JLabel("Mass 2 (kg):"); panel.add(label_mass2); field_mass2 = new JTextField(); panel.add(field_mass2); label_distance = new JLabel("Distance (m):"); panel.add(label_distance); field_distance = new JTextField(); panel.add(field_distance); button_calculate = new JButton("Calculate"); panel.add(button_calculate); label_result = new JLabel("Result:"); panel.add(label_result); frame.add(panel); frame.pack(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLocationRelativeTo(null); frame.setVisible(true); } private void bindEvents() { button_calculate.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { double mass1 = Double.parseDouble(field_mass1.getText()); double mass2 = Double.parseDouble(field_mass2.getText()); double distance = Double.parseDouble(field_distance.getText()); double gravity = 6.674 * Math.pow(10, -11); double force = gravity * mass1 * mass2 / Math.pow(distance, 2); label_result.setText(String.format("Result: %.2e N", force)); } }); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { new GravityCalculator(); } }); } } ``` 这个界面与上面的 Python 版本类似,使用了一个网格布局管理器来放置标签、文本框和按钮,并在点击 "Calculate" 按钮时计算重力力量并将结果显示在标签中。需要注意的是,按钮的事件处理器中要进行输入值的转换和计算,以及格式化结果的显示。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值