萌熊跑酷项目-26~30-手势控制

萌熊跑酷项目-手势控制

手势五种状态

public static class Consts  {
    //事件名字
    public const string E_ExitScenes = "E_ExitScenes";//ScenesArgs
    public const string E_EnterScenes = "E_EnterScenes";//ScenesArgs
    public const string E_StartUp = "E_StartUp";
    public const string E_EndGame = "E_EndGame";

    //model名字


    //view名字
    public const string V_PlayerMove = "V_PlayerMove";
    public const string V_PlayerAnim = "V_PlayerAnim";
}

//输入手势
public enum InputDirection {
    NULL,
    Right,
    Left,
    Down,
    Up
}

 手势判断

判断过程如图最上图

x方向的位移距离大于y轴方向的距离 时,左右移动     x>0向右,x<o向左

x方向的位移距离小于y轴方向的距离 时,上下移动     y>0向上,y<o向下

InputDirection m_inputDir = InputDirection.NULL;
    bool activeInput = false;//鼠标是否按下
    Vector3 m_mousePos;//鼠标按下的位置信息

    int m_nowIndex=1;
    int m_targetIndex=1;
    float m_xDistance;
    float m_yDistance;

    bool m_IsSlider = false;
    float m_SlideTime;
//获取输入
    void GetInputDirection() {
        //手势识别
        m_inputDir = InputDirection.NULL;
        if (Input.GetMouseButtonDown(0))
        {
            activeInput = true;
            m_mousePos = Input.mousePosition;
        }
        if (Input.GetMouseButton(0)&&activeInput)
        {
            Vector3 dir = Input.mousePosition - m_mousePos;
            if (dir.magnitude>20)
            {
                if (Mathf.Abs(dir.x)>Mathf.Abs(dir.y)&&dir.x>0)
                {
                    m_inputDir = InputDirection.Right;
                }
                else if (Mathf.Abs(dir.x) > Mathf.Abs(dir.y) && dir.x < 0)
                {
                    m_inputDir = InputDirection.Left;
                }
                else if (Mathf.Abs(dir.x) < Mathf.Abs(dir.y) && dir.y > 0)
                {
                    m_inputDir = InputDirection.Up;
                }
                else if (Mathf.Abs(dir.x) < Mathf.Abs(dir.y) && dir.y < 0)
                {
                    m_inputDir = InputDirection.Down;
                }
                activeInput = false;
            }
        }
        //键盘识别
        if (Input.GetKeyDown(KeyCode.W)||Input.GetKeyDown(KeyCode.Space))
        {
            m_inputDir = InputDirection.Up;
        }
        else if (Input.GetKeyDown(KeyCode.S))
        {
            m_inputDir = InputDirection.Down;
        }
        else if (Input.GetKeyDown(KeyCode.A))
        {
            m_inputDir = InputDirection.Left;
        }
        else if (Input.GetKeyDown(KeyCode.D))
        {
            m_inputDir = InputDirection.Right;
        }
    }

更新位置信息

角色在中时,m_nowIndex为1,左边时为0,右边时为2,用于指代角色所在赛道位置,赛道间距为2

 根据GetInputDirection()完成m_inputDir更新, 之后完成m_nowIndex等的值更新,最后发送消息SendMessage实现动画播放

/// <summary>
    /// 更新位置信息
    /// </summary>
    void UpdatePosition() {
        GetInputDirection();
        switch (m_inputDir)
        {
            case InputDirection.NULL:
                break;
            case InputDirection.Right:
                if (m_targetIndex<2)
                {
                    m_targetIndex++;
                    m_xDistance = 2;
                    SendMessage("AnimManager",m_inputDir);
                }
                break;
            case InputDirection.Left:
                if (m_targetIndex>0)
                {
                    m_targetIndex--;
                    m_xDistance = -2;
                    SendMessage("AnimManager",m_inputDir);
                }
                break;
            case InputDirection.Down:
                if (m_IsSlider==false)
                {
                    m_IsSlider = true;
                    m_SlideTime = 0.733f;
                    SendMessage("AnimManager", m_inputDir);
                }
                break;
            case InputDirection.Up:
                if (m_cc.isGrounded)
                {
                    m_yDistance = m_jumpValue;
                    SendMessage("AnimManager", m_inputDir);
                }
                break;
            default:
                break;
        }
    }

 更新完成位置后,真正实现位置移动

 //移动
    void MoveControl() {
        //左右移动
        if (m_targetIndex!=m_nowIndex)
        {
            float move = Mathf.Lerp(0,m_xDistance,m_moveSpeed*Time.deltaTime);
            transform.position += new Vector3(move,0,0);
            m_xDistance -= move;
            if (Mathf.Abs(m_xDistance)<0.05f)
            {
                m_xDistance = 0;
                m_nowIndex = m_targetIndex;
                switch (m_nowIndex)
                {
                    case 0:
                        transform.position = new Vector3(-2,transform.position.y,transform.position.z);
                        break;
                    case 1:
                        transform.position = new Vector3(0, transform.position.y, transform.position.z);
                        break;
                    case 2:
                        transform.position = new Vector3(2, transform.position.y, transform.position.z);
                        break;
                    default:
                        break;
                }
            }
        }

        if (m_IsSlider)
        {
            m_SlideTime -= Time.deltaTime;
            if (m_SlideTime<0)
            {
                m_IsSlider = false;
                m_SlideTime = 0;
            }
        }
    }

 动画播放

AnimManager总体动画控制,实现在PlayMove中使用发送消息控制动画播放,降低耦合

public class PlayerAnim : View {

    Animation anim;
    Action PlayAnim;
    
    public override string Name
    {
        get
        {
            return Consts.V_PlayerAnim;
        }
    }

    private void Awake()
    {
        anim = GetComponent<Animation>();
        PlayAnim = PlayRun;
    }

    private void Update()
    {
        if (PlayAnim!=null)
        {
            PlayAnim();
        }
    }

    void PlayRun() {
        anim.Play("run");
    }
    void PlayLeft() {
        anim.Play("left_jump");
        //normalizedTime动画播放进度,,,切换回向前跑的动画
        if (anim["left_jump"].normalizedTime>0.95f)
        {
            PlayAnim = PlayRun;
        }
    }
    void PlayRight() {
        anim.Play("right_jump");
        if (anim["right_jump"].normalizedTime>0.95f)
        {
            PlayAnim = PlayRun;
        }
    }
    void PlayRoll()
    {
        anim.Play("roll");
        if (anim["roll"].normalizedTime > 0.95f)
        {
            PlayAnim = PlayRun;
        }
    }
    void PlayJump()
    {
        anim.Play("jump");
        if (anim["jump"].normalizedTime > 0.95f)
        {
            PlayAnim = PlayRun;
        }
    }

    public void AnimManager(InputDirection dir) {
        switch (dir)
        {
            case InputDirection.NULL:
                break;
            case InputDirection.Right:
                PlayAnim = PlayRight;
                break;
            case InputDirection.Left:
                PlayAnim = PlayLeft;
                break;
            case InputDirection.Down:
                PlayAnim = PlayRoll;
                break;
            case InputDirection.Up:
                PlayAnim = PlayJump;
                break;
            default:
                break;
        }
    }


    public override void HandleEvent(string name, object data)
    {
        
    }
    
}

最终效果

屏幕上滑动或者键盘WASD控制位移移动,并播放对应的动画

图中向左移动,并播放向左移动动画

 

 

 

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
熊猫游戏中,我们已经实现了熊猫在一条无限循环的道路上奔,并且可以通过左右滑动来避开障碍物。现在我们要添加更多的游戏元素,使游戏更加有趣。 ## 1. 道路随机生成 我们要让游戏中的道路不再是一条固定的直线,而是随机生成的。我们可以通过几个步骤来实现这个功能: ### 1.1 创建地图 我们先创建一个地图类,用来表示游戏中的道路。地图由多个段组成,每个段包含一个随机生成的道路和一些障碍物。 ```swift class Map { var segments = [Segment]() init() { generateSegments() } func generateSegments() { // 生成一些段,每个段包含一个随机生成的道路和一些障碍物 } } ``` ### 1.2 随机生成道路 我们可以定义一个 `Road` 结构体,表示一段道路。道路由多个点组成,每个点包含一个 x 坐标和一个 y 坐标。 ```swift struct Road { var points = [CGPoint]() } ``` 在 `Map` 类中,我们可以实现一个方法来随机生成一段道路。我们可以先生成一些控制点,然后通过这些控制点计算出道路上的点。 ```swift func generateRoad() -> Road { let startX = CGFloat.random(in: -100...100) let startY = CGFloat.random(in: -100...100) let endX = CGFloat.random(in: -100...100) let endY = CGFloat.random(in: -100...100) let controlX1 = CGFloat.random(in: -100...100) let controlY1 = CGFloat.random(in: -100...100) let controlX2 = CGFloat.random(in: -100...100) let controlY2 = CGFloat.random(in: -100...100) let tValues = stride(from: 0, through: 1, by: 0.01) let points = tValues.map { t -> CGPoint in let x = pow(1-t, 3) * startX + 3 * pow(1-t, 2) * t * controlX1 + 3 * (1-t) * pow(t, 2) * controlX2 + pow(t, 3) * endX let y = pow(1-t, 3) * startY + 3 * pow(1-t, 2) * t * controlY1 + 3 * (1-t) * pow(t, 2) * controlY2 + pow(t, 3) * endY return CGPoint(x: x, y: y) } return Road(points: points) } ``` 这个方法会生成一个随机的起点和终点,以及两个控制点。然后使用贝塞尔曲线计算出道路上的点,并返回一个 `Road` 结构体。 ### 1.3 随机生成障碍物 在每个段中,我们还需要随机生成一些障碍物。我们可以定义一个 `Obstacle` 结构体,表示一个障碍物。障碍物由多个点组成,每个点包含一个 x 坐标和一个 y 坐标。 ```swift struct Obstacle { var points = [CGPoint]() } ``` 在 `Map` 类中,我们可以实现一个方法来随机生成一些障碍物。我们可以先生成一些控制点,然后通过这些控制点计算出障碍物上的点。 ```swift func generateObstacle() -> Obstacle { let startX = CGFloat.random(in: -50...50) let startY = CGFloat.random(in: -50...50) let endX = CGFloat.random(in: -50...50) let endY = CGFloat.random(in: -50...50) let controlX1 = CGFloat.random(in: -50...50) let controlY1 = CGFloat.random(in: -50...50) let controlX2 = CGFloat.random(in: -50...50) let controlY2 = CGFloat.random(in: -50...50) let tValues = stride(from: 0, through: 1, by: 0.01) let points = tValues.map { t -> CGPoint in let x = pow(1-t, 3) * startX + 3 * pow(1-t, 2) * t * controlX1 + 3 * (1-t) * pow(t, 2) * controlX2 + pow(t, 3) * endX let y = pow(1-t, 3) * startY + 3 * pow(1-t, 2) * t * controlY1 + 3 * (1-t) * pow(t, 2) * controlY2 + pow(t, 3) * endY return CGPoint(x: x, y: y) } return Obstacle(points: points) } ``` 这个方法会生成一个随机的起点和终点,以及两个控制点。然后使用贝塞尔曲线计算出障碍物上的点,并返回一个 `Obstacle` 结构体。 ### 1.4 生成多个段 有了上面两个方法,我们就可以在 `Map` 类中实现一个方法来生成多个段了。每个段包含一个随机生成的道路和一些障碍物。我们可以随机生成一些段,然后将这些段添加到 `segments` 数组中。 ```swift func generateSegments() { segments.removeAll() var lastEnd = CGPoint.zero for _ in 0..<10 { let road = generateRoad() let obstacleCount = Int.random(in: 1...3) var obstacles = [Obstacle]() for _ in 0..<obstacleCount { obstacles.append(generateObstacle()) } let segment = Segment(road: road, obstacles: obstacles, start: lastEnd) segments.append(segment) lastEnd = road.points.last! } } ``` 这个方法会生成 10 个随机的段,并将它们添加到 `segments` 数组中。每个段的起点都和上一个段的终点相连,形成一条无限循环的道路。 ### 1.5 显示地图 现在我们已经生成了一个随机的地图,接下来要将它显示在游戏中。我们可以在 `GameScene` 类中添加一个 `map` 属性,并在 `didMove(to:)` 方法中创建这个地图。 ```swift class GameScene: SKScene { var panda: Panda! var map: Map! override func didMove(to view: SKView) { panda = Panda() addChild(panda) map = Map() for segment in map.segments { let roadNode = SKShapeNode(splinePoints: segment.road.points, count: segment.road.points.count) roadNode.strokeColor = .white roadNode.lineWidth = 3 addChild(roadNode) for obstacle in segment.obstacles { let obstacleNode = SKShapeNode(splinePoints: obstacle.points, count: obstacle.points.count) obstacleNode.strokeColor = .red obstacleNode.lineWidth = 3 addChild(obstacleNode) } } } } ``` 在 `didMove(to:)` 方法中,我们先创建一个熊猫节点,并将它添加到场景中。然后创建一个地图对象,遍历地图中的每个段,创建一个道路节点和一些障碍物节点,并将它们添加到场景中。 现在运行游戏,就会看到一条随机生成的道路和一些随机生成的障碍物了! ## 2. 碰撞检测 我们已经随机生成了一些障碍物,现在要实现碰撞检测,当熊猫碰到障碍物时,游戏就结束。 ### 2.1 添加物理引擎 要进行碰撞检测,我们需要使用 SpriteKit 中的物理引擎。在 `GameScene` 类中,我们可以添加一个物理世界,并将熊猫和障碍物添加到这个世界中。 ```swift class GameScene: SKScene { var panda: Panda! var map: Map! var world: SKPhysicsWorld! override func didMove(to view: SKView) { panda = Panda() addChild(panda) map = Map() for segment in map.segments { let roadNode = SKShapeNode(splinePoints: segment.road.points, count: segment.road.points.count) roadNode.strokeColor = .white roadNode.lineWidth = 3 addChild(roadNode) for obstacle in segment.obstacles { let obstacleNode = SKShapeNode(splinePoints: obstacle.points, count: obstacle.points.count) obstacleNode.strokeColor = .red obstacleNode.lineWidth = 3 addChild(obstacleNode) let physicsBody = SKPhysicsBody(polygonFrom: obstacle.points) physicsBody.categoryBitMask = 1 physicsBody.contactTestBitMask = 2 obstacleNode.physicsBody = physicsBody } } physicsWorld.gravity = CGVector(dx: 0, dy: -9.8) physicsWorld.contactDelegate = self world = physicsWorld } } ``` 在 `didMove(to:)` 方法中,我们先创建一个熊猫节点,并将它添加到场景中。然后创建一个地图对象,遍历地图中的每个障碍物,创建一个障碍物节点,并将它添加到场景中。同时为障碍物添加一个物理体,并设置它的分类掩码和接触测试掩码。最后设置物理世界的重力和接触代理。 ### 2.2 检测碰撞 在 `GameScene` 类中,我们还需要实现 `SKPhysicsContactDelegate` 协议中的 `didBegin(_:)` 方法,检测熊猫和障碍物之间的碰撞。 ```swift class GameScene: SKScene, SKPhysicsContactDelegate { // ... func didBegin(_ contact: SKPhysicsContact) { if contact.bodyA.node == panda || contact.bodyB.node == panda { gameOver() } } func gameOver() { print("Game Over") // 处理游戏结束 } } ``` 在 `didBegin(_:)` 方法中,我们检查熊猫和障碍物是否发生了碰撞,如果发生了碰撞,就调用 `gameOver()` 方法,处理游戏结束的逻辑。 ### 2.3 开启碰撞检测 最后,我们还需要在 `Panda` 类中开启碰撞检测。 ```swift class Panda: SKSpriteNode { init() { // ... let physicsBody = SKPhysicsBody(texture: texture!, size: size) physicsBody.categoryBitMask = 2 physicsBody.contactTestBitMask = 1 physicsBody.collisionBitMask = 0 physicsBody.affectedByGravity = true physicsBody.allowsRotation = false physicsBody.velocity.dy = 200 physicsBody.angularVelocity = 0 physicsBody.linearDamping = 0 physicsBody.angularDamping = 0 physicsBody.restitution = 0 physicsBody.friction = 0 physicsBody.mass = 1 self.physicsBody = physicsBody } } ``` 在 `Panda` 类的初始化方法中,我们创建一个物理体,并设置它的分类掩码、接触测试掩码、碰撞掩码、重力、旋转等属性。最后将物理体添加到熊猫节点上。 现在运行游戏,当熊猫碰到障碍物时,游戏就会结束了! ## 3. 总结 在本章中,我们学习了如何让游戏中的道路随机生成,并实现了碰撞检测的功能。下一章中,我们将继续完善游戏,添加更多的功能和效果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值