(iOS)SpriteKit 制作简易手游虚拟摇杆(UIKit通用) Double零组件系列

本文适合SpriteKit初心者 源码:https://github.com/jamesdouble/JDGamePaddle

虚拟摇杆


16年,MOBA手游市场打得可火热(这里咱不帮任何游戏背书),撸瑟如我也是在16年开始玩SpriteKit,也就是苹果自制的2D游戏框架。 有玩过MOBA手游的,一定十分熟悉游戏里我们是如何移动主人公的吧?没玩过的......咱们不啰唆,先上图。

这样想不起来的也想起来了呗,因为SpriteKit本身并没有提供像摇杆这样元件(有提供反而奇怪),为了以防朋友们采坑(也没有很多坑),我就写了一个简易的摇杆Lib,SKScene,SKView(SpriteKit的UIView), UIView皆可直接加上。

简单的出发


一开始若把问题想得太复杂,那歪路肯定会走的特别的多,我们就照着上面的图来刻吧,外面一个圆圈,嗯,里面一个小圆.....好,结束了 ,就从拉出两个圆来出发吧!!

组件1:SKShapeNode


For SpriteKit初心者: UIKit里每个元件几乎都是UIView的子类别,而SpriteKit里的每个元件几乎都是SKNode的子类别。

那SKShapeNode又是什么?它是SKNode的子类别,若是你不是要用图片来初始化一个物件而是纯几何形状或是线条的话,使用它来做Node会快上许多,看它的建构值就能知道,真的很“几何”。

来吧!第一步!两个圆形!

MovingPing = SKShapeNode(circleOfRadius: paddleSize.width * 0.18)
MovingPing?.fillColor = UIColor.black  //内圆黑色实心
PaddleBorder = SKShapeNode(circleOfRadius: paddleSize.width * 0.5 )
PaddleBorder?.fillColor = UIColor.clear //外圆中间空心
PaddleBorder?.strokeColor = UIColor.black
复制代码

组件2:点击、滑动


Hmm.... We almost here, right? 不过现在你不管怎么拨弄它,它都不会移动的,在SpriteKit里常做的处理点击方式是实作覆写UIResponder大家常见的Method。


override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let touch = touches.first!
        touching = true
        let position = touch.location(in: self)
        if((PaddleBorder!.contains(position))) //若是手指还在边界内
        {
            MovingPing?.position = touch.location(in: self) //内圆移到手指位置
        }
        didMove()
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        let touch = touches.first!
        let postion = touch.location(in: self)
        if((PaddleBorder!.contains(postion)))
        {
            MovingPing?.position = postion
        }
     
        /*
        略
        */
    }
复制代码

结束了吗?执行起来试试吧。看起来听生硬的对吧,若是有MOBA手游只做到这边,主人公一定是脚抽了筋。会发生这样的杯具是在touchesMoved只判断是否手指在外圈内,而忽略了若是使用者从内圈滑到外圈的情况。

让我们把上面那段注解换成

    else{
            movingpaddleSmoothly(position: postion)
        }
  //算出边界外手指跟中心连成一线后,跟边界的交点。
    func movingpaddleSmoothly(position:CGPoint)
    {
        let centerPoint = PaddleBorder!.position
        let radius = self.frame.width * 0.5
        var distance:CGFloat = 0
        let diffx:CGFloat = (centerPoint.x - position.x) * (centerPoint.x - position.x)
        let diffy:CGFloat = (centerPoint.y - position.y) * (centerPoint.y - position.y)
        distance = sqrt(diffx + diffy)
        let ratio:CGFloat = radius/distance
        let newPostition:CGPoint = CGPoint(x: position.x * ratio, y: position.y * ratio)
        MovingPing?.position = newPostition
        
    }
复制代码

最后我们再每次移动摇杆时,回传一个经过正规化的CGVector出去给委任,以便使用者能随着滑动摇杆,做出适当的回应。

转载于:https://juejin.im/post/5ae2bb99f265da0b9d77f0e5

在一些手机游戏中,玩家可以通过虚拟控制盘来控制游戏角色的行动。 无人机和玩具操控App中也有这一类控制盘的应用。用自定义View的方式来实现类似手柄的控件。相关代码请见 github.com/RustFisher/…JoystickView特性目前JoystickView特性如下2种风格固定控制盘;浮动跟随模式;控制盘会移动到手指第一次点击的地方可以在背景上添加“箭头”,即添加效果图片自定义的“触摸球”图片和背景图片手指移出了控制盘范围,仍然能够保持追随能获取到移动位置的百分比参数实现思路用自定义View的方式实现这个控制盘。创建TouchView。控制盘的基本要求是跟随手指做出反应。为了获取到手指触屏的坐标,会用到View的onTouchEvent方法。控件中的“触摸球”和背景由图片得来。在自定义view中先获取相应的bitmap,缩放成指定的尺寸。onTouchEvent中获取到相应的坐标,计算出图片应该出现的位置;onDraw中根据坐标进行绘制。 计算出手指位置与控制盘中心的距离等信息,通过listener传递出去。代码示例样式设定有固定和浮动这两种风格,未来可能还会添加public enum PadStyle {     FLOATING /* 随用户手指重新定位 */,     FIXED    /* 固定位置 */ }控制盘配置我们可以不直接操作TouchView,创建TouchViewModel存放相关的配置。    private int bgResId;           // 背景图片资源ID     private int touchBmpResId;     // 触摸图资源ID - 例如一个圆球     private int directionPicResId; // 指示当前触摸点与圆心相对方向的图片ID     private float mWholeViewWid;    // 整个View的宽     private float mWholeViewHeight; // 整个View的高     private float mWholePadWid;    // 盘的宽度,包括箭头;并不是View的总宽度     private float mWholePadHeight; // 盘的高度,包括箭头;并不是View的总宽度     private int mRoundBgRadius;    // 背景圆的半径 背景圆位置可以变化     private int mTouchBallRadius = 100; // 触摸球的半径     private int mRoundBgPadding;   // 背景圆到Pad边界的px  一般是留给方向箭头的位置     private boolean showDirectionPic = false;    // 是否显示指示图片     private PadStyle mPadStyle = PadStyle.FIXED; // 默认为固定位置的     private PadLocationType mPadLocationType = PadLocationType.LEFT_BOT;     // .........控制盘管理器控制盘的配置项比较多,抽象出一个DefaultController来管理控制盘。这个控制器不是必要的。 管理器需要控制盘所在的父View,这里用的是RelativeLayout。创建一个“左控制盘”。将各个尺寸配置传入。最后添加到containerView中。    private void createLeftControlTouchView() {         TouchViewModel model = new TouchViewModel(                 R.drawable.ui_pic_joystick_left_pad,                 R.drawable.ui_pic_joystick_control_ball);         model.setWholeViewSize(ctx.getResources().getDimensionPixelSize(R.dimen.ui_joystick_whole_field_wid),                 ctx.getResources().getDimensionPixelSize(R.dimen.ui_joystick_whole_field_height));         model.setPadSize(ctx.getResources().getDimensionPixelSize(R.dimen.ui_joystick_pad_size),                 ctx.getResources().getDimensionPixelSize(R.dimen.ui_joystick_pad_size));         int roundBgRadius = ctx.getResources().getDimensionPixelSize(R.dimen.ui_joystick_round_bg_radius);         model.setContentSize(roundBgRadius, (int) (roundBgRadius / 3.5));         model.setStyle(padStyle, PadLocationType.LEFT_BOT);         model.setRoundBgPadding(ctx.getResources().getDimensionPixelSize(R.dimen.ui_joystick_circle_bg_padding));         leftControlTouchView = new TouchView(ctx);         leftControlTouchView.init(model);         // View的总大小         RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(                 ctx.getResources().getDimensionPixelSize(R.dimen.ui_joystick_whole_field_wid),                 ctx.getResources().getDimensionPixelSize(R.dimen.ui_joystick_whole_field_height)         );         params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);         params.addRule(RelativeLayout.ALIGN_PARENT_LEFT);         leftControlTouchView.setLayoutParams(params);     }     // ............     createLeftControlTouchView();     containerView.addView(leftControlTouchView);管理器初始化时需要一个ViewGroup来承载控制盘。    public DefaultController(Context context, RelativeLayout containerView, PadStyle padStyle) {         this.ctx = context;         this.containerView = containerView;         this.padStyle = padStyle;     }Fragment中使用初始化管理器初始化管理器,创建控制盘    mDefaultController =             new DefaultController(getContext(),                     (RelativeLayout) root.findViewById(R.id.joystick_container));     mDefaultController.createViews();     mDefaultController.showViews(false);设置监听器,获取用户的操作信息通过控制器来设置监听器        mDefaultController.setLeftTouchViewListener(new JoystickTouchViewListener() {             @Override             public void onTouch(float horizontalPercent, float verticalPercent) {                 Log.d(TAG, "onTouch left: "   horizontalPercent   ", "   verticalPercent);             }             @Override             public void onReset() {                 Log.d(TAG, "onReset: left");             }             @Override             public void onActionDown() {                 Log.d(TAG, "onActionDown: left");             }             @Override             public void onActionUp() {                 Log.d(TAG, "onActionUp: left");             }         });至此,我们实现了一个简单的控制盘控件。在一些控制类应用中可以使用这个控件。若想要做出更优美,更吸引人的控件,需要我们有好的审美水平。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值