unity 物理台球_使用基于Android物理的动画制作类似于桌球的游戏

unity 物理台球

学习Android开发 (Learning Android Development)

While playing around with Android Physics-based Animation API, I just created a simple App (just one Activity class— not good programming practice, but to state how simple it is) with about 200 lines of codes, and have the snooker like game.

W¯¯往往微不足道玩弄基于物理学的Android动画API ,我就创建了一个简单的应用程序(只需一个活动讲座不是良好的编程习惯,但是国家是多么简单),与约200行代码,并且有一个类似的游戏斯诺克。

基于Android物理的动画 (Android Physics-Based Animation)

Android Physics-Based Animation consists of these two animations, i.e. FlingAnimation and SpringAnimation, which is a subclass of DynamicAnimation.

基于Android物理的动画由这两个动画组成,即FlingAnimationSpringAnimation ,它们是DynamicAnimation的子类。

It is not provided by default on Android. You’ll need to get the dependencies through build.gradle by adding

Android上默认不提供此功能。 您需要通过添加以下内容来通过build.gradle获取依赖build.gradle

implementation "androidx.dynamicanimation:dynamicanimation:1.0.0"

Unlike normal animation, Fling and Spring animation behave according to the velocity provided to them. Hence the behavior is dynamic in correspond to its start velocity.

与普通动画不同,Fling和Spring动画根据提供给它们的速度进行动作。 因此,行为与其初始速度相对应是动态的。

I use both of them and tied them together to make this simple snooker-like game. Check out below for a little more details of how it is implemented.

我将它们都使用,并将它们绑在一起,以制作这款简单的斯诺克式游戏。 请在下面查看有关其实施方式的更多详细信息。

手势检测 (Gesture Detection)

The only touch control I add on is the gestureDetector

我添加的唯一触摸控件是gestureDetector

img_ball.setOnTouchListener { view, motionEvent ->
gestureDetector.onTouchEvent(motionEvent)
true}

Upon detecting the Fling gesture, we could then get both the X and Y velocity. Using those velocities, we’ll start the FlingAnimations

一旦检测到Fling手势,我们就可以同时获得X和Y速度。 使用这些速度,我们将启动FlingAnimations

private val gestureListener = object : GestureDetector.SimpleOnGestureListener() {
override fun onDown(e: MotionEvent?): Boolean {
stopAnimation = false
return true
}
override fun onFling(
e1: MotionEvent?,
e2: MotionEvent?,
velocityX: Float,
velocityY: Float
): Boolean {
if (isAnimationRunning()) return false
flingAnimationX.setStartVelocity(velocityX).start()
flingAnimationY.setStartVelocity(velocityY).start()
return true
}
}

动态动画 (Fling Dynamic Animation)

This is the most important behavior of the game. One could just click and swipe the ball towards the direction it should head and the velocity of the fling based on the gesture of the user.

这是游戏中最重要的行为。 只需单击并朝着用户应前进的方向滑动球,然后根据用户的手势挥动速度即可。

Unlike any other animation, the developer doesn’t define the target final destination, but instead, it is determined by the velocity provided with the frictions setting.

与其他动画不同,开发人员没有定义目标最终目标,而是由摩擦设置提供的速度来确定。

Notice the below GIF showing how the ball flow along the per the fling strength of the user.

请注意下面的GIF,该图显示了球如何沿用户的击球强度流动。

private fun instantiateFlingAnimation(
max: Float, animationType: DynamicAnimation.ViewProperty,
springAnimation: SpringAnimation, resetVelocity: (Float) -> Unit
): FlingAnimation {
return FlingAnimation(img_ball, animationType)
.setFriction(DEFAULT_FRICTION).apply {
setMinValue(0f)
setMaxValue(max)
addEndListener { _, _, _, velocity ->
resetVelocity(0f)
startStringAnimation(
velocity, springAnimation, springForce, max)
}
addUpdateListener { _, _, velocity ->
resetVelocity(velocity)
if (isSlowEnoughToEnterHole())
endCheck()
}
}
}

The Fling Animation above is applied to both X and Y direction individually.

上面的翻转动画分别应用于X和Y方向。

  • Both of them are set the DEFAULT_FRICTION so that it is equally smooth

    都将它们都设置为DEFAULT_FRICTION ,以使其同样平滑

  • Set Max and Min so it doesn’t go beyond the screen size

    设置最大和最小,使其不会超出屏幕尺寸
  • When the fling ends if there’s velocity (only happens when it hits the boundary of the screen), it will continue on with the Spring Animation

    当猛冲以一定velocity结束时(仅在其到达屏幕边界时发生),它将继续使用Spring Animation

  • While moving, constantly check if it already enters the hole when the velocity is slower than a certain threshold. Fast-rolling ball won’t enter the hole. (notice one of the shots above will roll over the hole)

    在移动时,不断检查其速度是否低于某个阈值,是否已经进入Kong中。 快滚球不会进入洞。 (请注意,上面的镜头之一将滑过洞)

弹簧动态动画 (Spring Dynamic Animation)

In actual snooker, there won’t be any Spring, instead, the ball will bounce back. But just to practice the behavior of Spring in Android Physics-Based Animation, I make the ball spring bouncing when it hits the wall.

在实际的斯诺克球中,不会有任何弹簧,而是球会反弹回来。 但是,只是为了在基于Android Physics的动画中练习Spring的行为,我使球弹簧碰到墙时弹起。

Check out the GIF below. It shows the ball bouncing in spring-like after hitting the wall.

查看下面的GIF。 它显示了撞到墙后的球像弹簧一样弹跳。

unity 物理台球_使用基于Android物理的动画制作类似于桌球的游戏_第1张图片

The Spring as shown in the Fling code above is triggered at the end of Fling.

上面的Fling代码中所示的Spring在Fling结束时触发。

private fun startStringAnimation(
velocity: Float, springAnimation: SpringAnimation,
springForce: SpringForce, max: Float
) {
if (abs(velocity) > 0 && !stopAnimation) {
springAnimation
.setSpring(springForce.setFinalPosition(
if (velocity > 0) max else 0f))
.setStartVelocity(velocity)
.start()
}
}

It should also start the Spring Animation when there’s still remaining velocity from the Fling to pass over.

当仍然有来自Fling的剩余速度可以通过时,它还应该启动Spring动画。

Other than the velocity, there’s the SpringAnimation which takes the SpringForce provided. These two will be further elaborated below. The SpringForce is also used to set the final position of the ball to settle down after the Spring effect.

比速度等,有这需要提供的弹簧力SpringAnimation。 这两个将在下面进一步阐述。 SpringForce也可用于设置球的最终位置,使其在弹簧效果后落下来。

弹簧力 (Spring Force)

In Spring, it has a Stiffness (how flexible it is) the and Damping Ratio (how bouncy it is). It is a float number settings with some constants predefined we can use. They are defined in the SpringForce.

在Spring,它具有“刚度”(柔韧性)和“阻尼比”(弹性)。 这是一个浮点数设置,其中包含一些我们可以使用的预定义常量。 它们在SpringForce中定义。

private val springForce: SpringForce
get() = SpringForce(0f).apply {
stiffness = SpringForce.STIFFNESS_LOW
dampingRatio
= SpringForce.DAMPING_RATIO_HIGH_BOUNCY
}

Other than stiffness and dampingRatio, one can set the final destination on it as well, which we have seen set when the SpringAnimation began.

除了刚度阻尼比,还可以在其上设置最终目标,我们在SpringAnimation开始时就已经看到了最终目标。

Spring动画 (Spring Animation)

The SpringAnimation define the attribute of the Spring, and will be given the SpringForce. However, the SpringForce is not setup during initialization because we haven’t know the final destination of it yet (i.e. which border it will hit).

SpringAnimation定义Spring的属性,并将被赋予SpringForce 。 但是, SpringForce尚未在初始化期间设置,因为我们尚不知道它的最终目的地(即它将撞到哪个边界)。

private fun instantiateSpringAnimation(
animationType: DynamicAnimation.ViewProperty?,
resetVelocity: (Float) -> Unit
): SpringAnimation {
return SpringAnimation(img_ball, animationType).apply {
addEndListener { _, _, _, _ -> resetVelocity(0f) }
addUpdateListener { _, _, velocity ->
resetVelocity(velocity)
if (isSlowEnoughToEnterHole())
endCheck()
}
}
}

The Spring Animation above is applied to both X and Y direction individually. (in case it hits a corner wall, it can bounce both ways). Just like Fling Animation, it also checks if it has hit the hole, and has velocity slow enough to enter the hole.

上面的Spring动画分别应用于X和Y方向。 (万一碰到墙角,它可能会双向反弹)。 就像Fling Animation一样,它还会检查它是否击中了Kong,并且速度是否足够慢以进入Kong。

进入Kong (Entering Hole)

There are various considerations before one could determine if it has entered the hole.

在确定它是否已进入Kong之前,有多种考虑。

它的速度足够慢吗? (Is it at a slow enough velocity?)

This is important as a fast-moving ball won’t stop to enter a hole as the gravity fails to pull it down.

这很重要,因为快速移动的球不会因重力未能将其拉下而停下来进入Kong。

private fun isSlowEnoughToEnterHole(): Boolean { return 
abs(velocityFlingY) < VELOCITY_THRESHOLD &&
abs(velocityFlingX) < VELOCITY_THRESHOLD &&
abs(velocitySpringY) < VELOCITY_THRESHOLD &&
abs(velocitySpringX) < VELOCITY_THRESHOLD
}

To verify the speed, we need to verify the velocities from all the animations to ensure the velocities of them all are under the threshold. This is to avoid one entering a hole just because it is not moving (0 velocity) in a certain direction only but moving fast in another direction.

为了验证速度,我们需要验证所有动画的速度,以确保所有动画的速度都在阈值以下。 这是为了避免一个人仅仅因为它不仅仅沿某个方向移动(0速度)而是沿另一个方向快速移动而进入Kong。

它进入洞了吗? (Is it entering the hole?)

To check if it is entering the hole, we need to get the center of the call as the measurement. And also the isEnding check is there in case if there are multiple directions it is slow enough to enter the hole, we just need to have one check.

要检查它是否正在进入Kong,我们需要获取调用的中心作为度量。 另外,如果有多个方向,如果要足够慢地进入Kong,则也要进行isEnding检查,我们只需要进行一次检查即可。

private fun endCheck() {
if (isEnding) return
val ballCenterX = img_ball.x + img_ball.width / 2
val ballCenterY = img_ball.y + img_ball.height / 2
if (holes.any { isEnteringHole(it, ballCenterX, ballCenterY) }){
Toast.makeText(this, "Congratulation!",
Toast.LENGTH_SHORT).show()
}
}

After getting the center of the ball coordinate, we need to see if the coordinate is within the hole. I use simple rectangular matching. Ideally one should use a radius to calculate for more accuracy. Nonetheless, the error is pretty small.

获取球坐标的中心后,我们需要查看坐标是否在Kong内。 我使用简单的矩形匹配。 理想情况下,应使用半径进行计算以提高准确性。 但是,错误很小。

private fun isHittingTarget(ballCenterX: Float, hole: ImageView, ballCenterY: Float) =
(ballCenterX >= hole.x && ballCenterX <= hole.x + hold.width) &&
(ballCenterY >= hole.y && ballCenterY <= hole.y + hold.height)

After entering the hole, we need to end all animation but perform a new animation that shows the ball entering the hole.

进入Kong后,我们需要结束所有动画,但是执行一个新动画以显示球进入Kong。

private fun isEnteringHole(hole: ImageView, ballCenterX: Float, ballCenterY: Float): Boolean {
if (isHittingTarget(ballCenterX, hole, ballCenterY)) {
isEnding = true
endAllPhysicAnimation()
animateBallIntoHole(hold)
return true
}
return false
}

对进入球洞的球进行动画处理。 (Animate the ball entering the hole.)

Lastly, to have a nice way of showing the ball entering the hole, we perform several animations.

最后,为了有一种很好的方式来显示球进入球洞的方式,我们执行了几个动画。

  1. Translation animation, that moves both X and Y position of the ball towards the center of the hole.

    平移动画,将球的X和Y位置都移向Kong的中心。
  2. Perform a shrinking (inverse scaling) of the ball together with the fading away (reduce the transparency of the ball), which makes it like dropping into the hole

    对球进行收缩(反比例缩放)并进行渐隐(减小球的透明度),这使其像掉入Kong中一样
unity 物理台球_使用基于Android物理的动画制作类似于桌球的游戏_第2张图片
private fun animateBallIntoHole(hole: ImageView) {
AnimatorSet().apply {
play(
ObjectAnimator.ofPropertyValuesHolder(
img_ball,
PropertyValuesHolder.ofFloat(View.ALPHA, 1f, 0f),
PropertyValuesHolder.ofFloat(View.SCALE_X, 1f, 0.5f),
PropertyValuesHolder.ofFloat(View.SCALE_Y, 1f, 0.5f)
)
).after(
ObjectAnimator.ofPropertyValuesHolder(
img_ball,
PropertyValuesHolder.ofFloat(View.X, hole.x),
PropertyValuesHolder.ofFloat(View.Y, hole.y)
)
)
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
reappearBall()
}
})
}.start()
}

The animation completed, then show the ball re-appear in the middle.

动画完成后,显示球重新出现在中间。

private fun reappearBall() {
img_ball.translationX = 0f
img_ball.translationY = 0f
img_ball.scaleX = 1f
img_ball.scaleY = 1f
flingAnimationX.friction = DEFAULT_FRICTION
flingAnimationY.friction = DEFAULT_FRICTION
ObjectAnimator.ofFloat(img_ball, View.ALPHA, 0f, 1f).apply {
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
isEnding = false
}
})
}.start()
}

To get some read about basic animator animations, check out the below two blogs.

要获得有关基本动画师动画的一些信息,请查看下面的两个博客。

放在一起 (Putting all together)

When we combine them all together, with the Fling, Spring, and the Entering the hole animation together, the below sum it all nicely.

当我们将它们全部组合在一起时,包括Fling,Spring和Entering the hole动画,下面将它们进行总结。

  1. The ball flings straight into the corner hole

    球直冲角球
  2. Hitting the corner hole, the Spring effect take place both in X and Y axis

    击中角Kong时,弹簧效果同时发生在X和Y轴上
  3. And then slowly circle against the hole and entering the hole with translation.

    然后慢慢绕Kong打圈,然后平移进入Kong。
unity 物理台球_使用基于Android物理的动画制作类似于桌球的游戏_第3张图片

One little detail didn’t mention above is, upon ending, we stop the Spring Animation, and slow down the Fling Animation by increasing the friction to a higher number.

上面没有提到的一个小细节是,结束时,我们停止了Spring Animation,并通过增加更大的摩擦来减缓Fling Animation。

private fun endAllPhysicAnimation() {
springAnimationX.cancel()
springAnimationY.cancel()
flingAnimationY.friction = BREAK_FRICTION
flingAnimationX.friction = BREAK_FRICTION
stopAnimation = true
}

Hence this produces the nice slow down of movement around the hole and with a mixture of Fling and Spring and Translation, showing the nice effect.

因此,这将产生很好的减慢Kong运动的效果,并结合了Fling和Spring和Translation,显示出很好的效果。

That’s it. Simple animation made it so fun. You can get the code here.

而已。 简单的动画使它变得如此有趣。 您可以在此处获取代码 。

演示地址

翻译自: https://medium.com/mobile-app-development-publication/make-snooker-like-game-with-android-physics-based-animation-663baf74ffe5

unity 物理台球

你可能感兴趣的:(游戏,unity,游戏开发,android,python)