Unity 实现Slot Machine两种转动效果

1 篇文章 0 订阅


最近在学习unity的物体移动,刚好某铁玩到了黄金时刻的玩法,想自己试一下实现出来;
在这里插入图片描述

废话不多说,先上效果图
请添加图片描述

玩法介绍

这种玩法一般都是有三列图案组成,转动停止后三列图案一致时即可得到奖励/胜利;
每个策划对这个转动效果的定义都会不一样,有喜欢缓慢停止的,通过缓慢停止塑造一种紧张感;有突然停止回弹的,塑造转动的爽感;视觉效果会影响玩家游戏时的心情,我个人更倾向于后者,更能突出视觉的爽感;

技术实现

思考

1、使用一个总控脚本控制n个Slot单元,给slot单元发出需要转动的位置,控制是否能spin,判断输赢等;
2、使用一个Slot单元控制slot单元转动,只管接收总控的目标值进行移动,不做额外的功能;

总控代码

SlotMachine3.cs

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Random = UnityEngine.Random;

public class SlotMachine3 : MonoBehaviour
{
    public Slot3[] slots; // slot单元数组
    public bool isSpin; // 判断当前是否在转动
    public float[] slotResult; // Slot的结果

    private int _stoppedSlots; // 记录停止的slot数量
    private const string SpinMethodName = nameof(Spin);
    public Dictionary<float, string> slotItems; // slot的位置数据
    private Dictionary<string, int> _slotBet; // slot的倍率

    // 录入数据
    private void MapSlotItems()
    {
        slotItems = new Dictionary<float, string>
        {
            { -5.25f, "Diamond" },
            { -3.75f, "Crown" },
            { -2.25f, "Watermelon" },
            { -0.75f, "Bar" },
            { 0.75f, "Seven" },
            { 2.25f, "Cherry" },
            { 3.75f, "Lemon" },
            { 5.25f, "Diamond" }
        };
    }

    private void MapSlotBet()
    {
        _slotBet = new Dictionary<string, int>
        {
            { "Seven", 500 },
            { "Bar", 150 },
            { "Crown", 100 },
            { "Diamond", 50 },
            { "Cherry", 25 },
            { "Watermelon", 10 },
            { "Lemon", 5 }
        };
    }


    /// <summary>
    /// 转动spin
    /// </summary>
    public void Spin()
    {
        // 如果当前在转动,则跳出函数
        if (isSpin) return;
        // 生成开奖结果,指导slot转动
        GenerateResult();
        isSpin = true;
        foreach (var slot in slots)
        {
            // 转动所有slot单元
            slot.StartCoroutine(SpinMethodName);
        }
    }

    /// <summary>
    /// 等待所有slot停止并检查结果
    /// </summary>
    public void WaitResults()
    {
        // 每当一个slot调用则证明停止
        _stoppedSlots--;
        // 当最后一个停止时,重置这个停止的监控
        if (_stoppedSlots > 0) return;
        _stoppedSlots = ResetStoppedSlotsCount();
        // 调用结果对比方法
        CheckResults();
    }

    /// <summary>
    /// 校验数组元素是否一致
    /// </summary>
    /// <param name="resultNameList">需要校验的结果数组</param>
    /// <returns></returns>
    private static bool CheckResultIsOnlyOneItem(string[] resultNameList)
    {
        return resultNameList.Distinct().Count() == 1;
    }

    /// <summary>
    /// 检查结果是否中奖
    /// </summary>
    private void CheckResults()
    {
        isSpin = false;
        var resultNameList = new string[3];
        var numCounter = 0;
        foreach (var key in slotResult)
        {
            resultNameList[numCounter] = slotItems[key];
            numCounter++;
        }

        // 检查数组是否满足元素唯一,唯一即中奖;
        if (!CheckResultIsOnlyOneItem(resultNameList)) return;
        var bet = _slotBet[resultNameList[0]];
        // 这里用倍率代表金币吧
        AddMoney(bet * 1);
    }

    private static void AddMoney(int money)
    {
        Debug.Log($"赢了 {money} 金币");
    }

    /// <summary>
    /// 生成结果数组
    /// </summary>
    private void GenerateResult()
    {
        // 清空数组
        slotResult = Array.Empty<float>();
        // 随机生成结果
        slotResult = slotItems.Keys.OrderBy(x => Random.value).Take(_stoppedSlots).ToArray();
        for (var i = 0; i < slotResult.Length; i++)
        {
            slots[i].targetY = slotResult[i];
        }
    }

    /// <summary>
    /// 查看需要监控多少个slot单元
    /// </summary>
    /// <returns>slot单元数组长度</returns>
    private int ResetStoppedSlotsCount()
    {
        return slots.Length;
    }
    
    private void Awake()
    {
        _stoppedSlots = ResetStoppedSlotsCount();
        MapSlotItems();
        MapSlotBet();
    }
}

unity对象结构

在这里插入图片描述

SlotMachine3

在这里插入图片描述

Row

在这里插入图片描述


缓慢减速效果

效果:期望转出n圈后,平滑减速直到item转动到总控发出的指定位置

Slot2.cs

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

// TODO:减速移动到指定位置

public class Slot2 : MonoBehaviour
{
    public float speed;
    public float targetY;


    private SlotMachine2 _sm2;
    private const string EndSpinMethodName = nameof(EndSpin);
    private const float SpeedDownRate = 0.85f;
    private const float StartY = -5.25f;
    private const float EndY = 5.25f;

    public IEnumerator Spin()
    {
        speed = 15f + Random.Range(10f, 20f);
        while (speed >= 10f)
        {
            transform.Translate(Vector2.up * Time.deltaTime * -speed);
            if (transform.localPosition.y <= StartY)
            {
                // 出结果之后才开始减速
                if (_sm2.slotResult.Length != 0) speed *= SpeedDownRate;
                transform.localPosition = new Vector2(transform.localPosition.x, EndY);
            }

            yield return null;
        }

        // 调用到这里的时候其实已经是从最初开始,调用End就是移动到指定距离了
        StartCoroutine(EndSpinMethodName);
        yield return null;
    }

    public IEnumerator EndSpin()
    {
        // 如果是转到第一个的diamond,那就让他转动到最后一个,主要是增加转动带来的视觉效果
        if (Mathf.Approximately(targetY, StartY)) targetY = EndY;
        while (speed >= 5f)
        {
            var position = transform.localPosition;
            speed -= SpeedDownRate * Time.deltaTime;
            position = Vector2.MoveTowards(position, new Vector2(position.x, targetY), speed * Time.deltaTime);
            transform.localPosition = position;
            if (new Vector2(position.x, position.y) == new Vector2(position.x, targetY)) break;
            yield return null;
        }

        speed = 0f;
        _sm2.WaitResults();
        yield return null;
    }


    private void RandomItem()
    {
        var randomY = _sm2.slotItems.Keys.OrderBy(x => Random.value).Take(1);
        transform.position = new Vector3(transform.position.x, randomY.Average(), transform.position.z);
    }

    private void Start()
    {
        _sm2 = GetComponentInParent<SlotMachine2>();
        RandomItem();
    }
}

惯性回弹效果

效果:期望速度不变的情况下转动到总控发出的指定位置+偏移值后急停,然后回弹到指定位置;

Slots3.cs

using System.Collections;
using System.Linq;
using UnityEngine;

// TODO: 不减速,回弹效果

public class Slot3 : MonoBehaviour
{
    public float targetY;

    private SlotMachine3 _sm3;
    private float _speed;
    private const float OffsetY = 0.8f; // 回弹的偏移值
    private const float BounceSpeed = 5f; // 回弹速度
    private const int SlotRound = 5; // 转的圈数
    private const string EndSpinMethodName = nameof(EndSpin); // 调用的方法名
    private const float StartY = 5.25f;
    private const float EndY = -5.25f;

    public IEnumerator Spin()
    {
        var spinRound = SlotRound;
        _speed = 15f + Random.Range(10f, 20f);
        while (spinRound > 0)
        {
            transform.Translate(Vector2.up * Time.deltaTime * -_speed);
            // 超出边界重置
            if (transform.localPosition.y <= EndY)
            {
                // 出结果之后才开始减速
                if (_sm3.slotResult.Length == 0) spinRound = SlotRound;
                transform.localPosition = new Vector2(transform.localPosition.x, StartY);
                spinRound--;
            }

            yield return null;
        }

        // 调用到这里的时候其实已经是从最初开始,调用End就是移动到指定距离了
        StartCoroutine(EndSpinMethodName);
        yield return null;
    }

    public IEnumerator EndSpin()
    {
        // 如果是转到第一个的diamond,那就让他转动到最后一个,主要是增加转动带来的视觉效果
        if (Mathf.Approximately(targetY, StartY)) targetY = EndY;

        Vector2 startPosition = transform.localPosition;

        // 按照原来的速度移动到目标后跳出循环
        while (transform.localPosition.y >= (targetY - OffsetY))
        {
            transform.Translate(Vector2.up * Time.deltaTime * -_speed);
            yield return null;
        }

        // 回弹到指定位置后再回到目标位置
        while (Vector2.Distance(transform.localPosition, new Vector2(startPosition.x, targetY)) > 0.01f)
        {
            transform.localPosition = Vector2.MoveTowards(transform.localPosition,
                new Vector2(startPosition.x, targetY), BounceSpeed * Time.deltaTime);
            yield return null;
        }

        _speed = 0f;
        _sm3.WaitResults();
    }


    private void RandomItem()
    {
        var randomY = _sm3.slotItems.Keys.OrderBy(x => Random.value).Take(1);
        transform.localPosition = new Vector3(transform.localPosition.x, randomY.Average(), transform.localPosition.z);
    }

    private void Start()
    {
        _sm3 = GetComponentInParent<SlotMachine3>();
        RandomItem();
    }
}

附录

美术资源(科学上网):SlotMachineArt.zip
图案坐标表
在这里插入图片描述

倍率表
在这里插入图片描述

Unity实现车方向盘转动效果可以通过以下步骤: 1. 首先,在车辆模型上添加一个空对象作为车方向盘。可以使用Unity编辑器中的GameObject->Create Empty命令来创建这个空对象。 2. 将方向盘空对象的位置和旋转与车辆模型对应起来,使其看起来像是连接在车辆驾驶座位上的真实方向盘。 3. 创建一个C#脚本,命名为“SteeringWheel”,并将其附加到车辆模型上。 4. 在脚本中,使用Unity的输入系统来获取玩家的输入。可以使用Input.GetAxis()函数来获取方向盘的输入值。例如,可以将水平轴输入映射到方向盘旋转的角度。 5. 在脚本中,使用Transform.Rotate()函数来旋转方向盘空对象。根据输入值的大小,可以通过乘以一个旋转速度因子来调整方向盘的旋转速度。 6. 在Update()函数中,不断更新方向盘的旋转角度,以反映玩家的输入。例如:transform.Rotate(0f,0f,-steeringInput * rotationSpeed * Time.deltaTime); 7. 在车辆模型上添加物理刚体组件,并使用刚体的AddTorque()函数来模拟车辆转向。在脚本中,将方向盘的输入映射到一个旋转力矩,然后将其应用于刚体。这可以通过使用刚体的transform.TransformDirection()函数来实现。例如:rigidbody.AddTorque(transform.TransformDirection(Vector3.up) * steeringInput * torqueFactor); 8. 在游戏中运行时,根据玩家的输入,车辆应该可以通过转动方向盘来实现转向的效果。 通过以上步骤,就可以在Unity实现车辆方向盘转动效果。可以根据具体的需求进一步调整和优化代码,以达到更好的效果
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值