首页 > 编程语言 >Unity从0开发游戏上架微信小程序系列-1-完成小游戏逻辑

Unity从0开发游戏上架微信小程序系列-1-完成小游戏逻辑

时间:2024-10-31 21:49:36浏览次数:6  
标签:篮子 游戏 微信 物体 private item Unity 小游戏 var

本人的小游戏“方块消了个消”已经上线啦!!!欢迎各位来体验,希望大家多多提意见哦~

微信公众号:unity学习加油站 ,

注:本系列更完会在公众号上放置源码,本系列会包括以下几种游戏

前言:本文主要内容,制作上图中第一个游戏,现在开始吧,喜欢的朋友点个赞吧!!!


第一步:准备素材,导入Unity中制作单个游戏形状预制件

形状预制件层级结构详解如下:


第二步:场景制作,BlockParent作为生成单个游戏物体的父物体,游戏物体的移动范围需要控制在这个父物体的显示范围内;

场景中新建一个Canvas,命名为PlayRoom,修改Canvas组件参数;

1,将渲染模式修改为摄像机模式,然后将主摄像机拖拽到渲染摄像机中;

2,修改Canvas Scaler的缩放模式,参考分辨率设置为720*1560,屏幕匹配模式设置为Match Width Or Height(宽度或高度适配),将匹配值修改为1(根据高度适配,因为是竖屏游戏)


第三步:实现单个游戏物体的拖拽和点击

1,创建脚本命名为RemoveBlockGamePlayRoom,将其拖拽到PlayRoom物体上

2,编写脚本实现点击和拖拽功能;

public Camera mainCamera;//用于获取屏幕坐标到世界坐标的转换。

private Vector3 downPos; //存储鼠标按下时的屏幕位置。

private bool isDragging; //标记是否正在拖拽对象。

private SingleBlockItem chooseBlockItem; //当前选中的 SingleBlockItem 对象

当鼠标左键按下时,重置 chooseBlockItem 和 isDragging。记录鼠标按下的位置 downPos。从 mainCamera 发射一条射线,检测是否有2D碰撞体被击中。如果有碰撞体被击中,尝试获取其父对象上的 SingleBlockItem 组件,并将其赋值给 chooseBlockItem。

if (Input.GetMouseButtonDown(0))
        {
            chooseBlockItem = null;
            isDragging = false;
            downPos = Input.mousePosition;
            var ray = mainCamera.ScreenPointToRay(downPos);
            var rayCastHit2D = Physics2D.Raycast(ray.origin, ray.direction);
            if (rayCastHit2D.transform != null)
            {
                rayCastHit2D.transform.parent.TryGetComponent(out chooseBlockItem);
            }
        }

如果鼠标左键一直按着,并且 chooseBlockItem 不为空: 检查鼠标当前位置与按下位置的距离是否大于5个单位。如果距离大于5个单位,设置 isDragging 为 true,表示开始拖拽,将鼠标当前位置转换为世界坐标(需要限制物体只能在一定范围内移动),将 chooseBlockItem 的位置更新为这个新的世界坐标;

if (Input.GetMouseButton(0))
        {
            if (chooseBlockItem != null)
            {
                // 当前位置与按下位置距离大于5时视为拖拽游戏物体移动,否则视为点击
                if (Vector3.Distance(downPos, Input.mousePosition) > 5)
                {
                    // 开始移动
                    isDragging = true;
                    var tempPos = mainCamera.ScreenToWorldPoint(Input.mousePosition);
                    // 现在游戏物体只能在父物体范围内移动
                    tempPos.z = 0;
                    tempPos.x = Mathf.Clamp(tempPos.x, -1.73f, 1.73f);
                    tempPos.y = Mathf.Clamp(tempPos.y, -2.21f, 3.07f);
                    chooseBlockItem.transform.position = tempPos;
                }
            }
        }

当鼠标左键释放时: 如果 chooseBlockItem 不为空且没有进行拖拽(即只是点击),调用 ChooseBlockItem 方法。重置 chooseBlockItem 和 isDragging。

if (Input.GetMouseButtonUp(0))
        {
            if (chooseBlockItem != null && !isDragging)
            {
                ChooseBlockItem(chooseBlockItem);//具体实现见下文
            }
            
            chooseBlockItem = null;
            isDragging = false;
        }

第四步:关卡制作,即游戏开始时在场景中随机生成游戏物体,根据要生成的组的数量(三个元素一组,三个相同的元素才能消除),每组先随机一个形状的物和颜色,然后每组生成三个,这个三个物体再随机设置位置;最后随机物体的层级显示,具体实现如下:

private int totalBlockCount; // 当前关卡生成的所有方块的数量
// 根据需要生成组的数量(一个游戏物体需要生成三个,因为消除时需要三个匹配才能消除),随机生成游戏物体在场景中
    private void LoadGame(int loadGroupCount)
    {
        totalBlockCount = loadGroupCount * 3;
        for (var i = 0; i < loadGroupCount; i++)
        {
            // 随机游戏物体的颜色和形状
            var randomColor = RemoveBlockGameConfig.Instance.ColorConfigs[Random.Range(0, RemoveBlockGameConfig.Instance.ColorConfigs.Count)];
            var randomShape = RemoveBlockGameConfig.Instance.BlockItems[Random.Range(0, RemoveBlockGameConfig.Instance.BlockItems.Count)];
            for (var j = 0; j < 3; j++)
            {
                var blockItem = Instantiate(randomShape, blockItemParent);
                // 随机游戏物体的位置,限制在父物体的范围内
                var randomPos = new Vector3(Random.Range(-1.73f, 1.73f), Random.Range(-2.21f, 3.07f), 0);
                blockItem.SetPos(randomPos, false);
                // 随机设置旋转和颜色
                blockItem.SetRotation(new Vector3(0, 0, Random.Range(0, 360f)));
                blockItem.SetColor(randomColor);
            }
        }

        // 再随机设置生成物体的覆盖层级
        for (var i = 0; i < blockItemParent.childCount; i++)
        {
            blockItemParent.GetChild(i).transform.SetSiblingIndex(Random.Range(0, blockItemParent.childCount - 1));
        }
    }

游戏的配置信息采用的是ScriptableObject存储,目前使用到的配置是游戏物体的形状和颜色;具体如下:

[CreateAssetMenu(fileName = nameof(RemoveBlockGameConfig), menuName = "SO/" + nameof(RemoveBlockGameConfig))]
public class RemoveBlockGameConfig : ScriptableObject
{
    private static RemoveBlockGameConfig instance;
    public static RemoveBlockGameConfig Instance
    {
        get
        {
            if (instance == null)
            {
                instance = Resources.LoadAsync<ScriptableObject>("SO/RemoveBlockGameConfig").GetAwaiter().GetResult() as RemoveBlockGameConfig;
            }
            return instance;
        }
    }

    // 配置游戏中能生成的游戏物体的形状
    public List<SingleBlockItem> BlockItems;
    // 配置游戏中能生成游戏物体的颜色
    public List<Color> ColorConfigs;
}

效果图如下:


第五步:实现物体的收集,点击物体即放置在收集篮内,实现效果如下:

1,制作收集篮预制体,创建一个空物体命名为BasketItemParent作为所有篮子的父物体,在其下面创建六个篮子

2,单个篮子的层级如下,外层一个背景,内层一个空物体用做放置的父物体,修改其缩放为0.58,以致能够容纳最大的游戏物体;

3,编写单个篮子的脚本,添加到每一个BasketItem物体上,具体如下:

public class SingleBasketItem : MonoBehaviour
{
    [SerializeField] private Transform blockParent;//放置方块的父物体
    public SingleBlockItem BlockItem { get; private set; }//当前篮子放置的方块
    public bool HasPutBlock => BlockItem != null;//判断当前篮子是否有篮子

    // 向当前篮子放置方块
    public void SetBlock(SingleBlockItem block)
    {
        if (block != null)
        {
            block.transform.SetParent(blockParent);
            block.SetBlockInteractable(false);    
        }
        
        BlockItem = block;
    }
}

4,实现点击方块进行收集;成功点击方块后,判断是否有空篮子,如果有空篮子,则将当前点击的方块放置对应的篮子中,对篮子进行排序,将相同类型的方块放置在一起;具体代码如下:

[SerializeField] private RectTransform basketParent; // 篮子父物体
    [SerializeField] private SingleBasketItem[] basketItems; // 所有的篮子
    [SerializeField] private ParticleSystem destroyEffect; // 消除特效
    
    private void ChooseBlockItem(SingleBlockItem blockItem)
    {
        if(blockItem == null) return;
        // 找到空篮子,将当前选择的放置在其中
        var tempBasket = GetEmptyBasket();
        if (tempBasket != null)
        {
            tempBasket.SetBlock(blockItem);
            
            // 判断插入的位置的index,将相同的元素放置在一起
            var insertIndex = -1;
            for (var i = basketItems.Length - 1; i >= 0 ; i--)
            {
                if (tempBasket != basketItems[i] && basketItems[i].HasPutBlock && basketItems[i].BlockItem.ShapeIndex == blockItem.ShapeIndex &&
                    basketItems[i].BlockItem.ColorIndex == blockItem.ColorIndex)
                {
                    insertIndex = basketItems[i].transform.GetSiblingIndex();
                    break;
                }
            }
            if (insertIndex >= 0)
            {
                tempBasket.transform.SetSiblingIndex(insertIndex + 1);
            }
            LayoutRebuilder.ForceRebuildLayoutImmediate(basketParent);
            blockItem.MoveTo(tempBasket.transform.position, () =>
            {
                // 判断是否有可以合成的元素,如果有,则进行消除,如果没有,则判断是否还有空篮子,如果没有则游戏失败
                CheckHasBlockCanRemove();
                CheckIsGameOver();
            });
        }
    }

CheckHasBlockCanRemove方法判断是否有满足消除条件(三个相同的可以进行消除)的篮子,如果有,则将对应的内容消除,然后对篮子进行排序,将非空篮子放置在最前面,具体代码如下:

// 检查篮子中是否有相同的元素可以进行消除
    private void CheckHasBlockCanRemove()
    {
        // 找到连续三个相同的形状和颜色进行消除
        var tempShapeIndex = -1;
        var tempColorIndex = -1;
        var sameBlockCount = 1;

        for (var i = 0; i < basketParent.childCount; i++)
        {
            var item = basketParent.GetChild(i).GetComponent<SingleBasketItem>();
            if (item.HasPutBlock)
            {
                if (tempShapeIndex == item.BlockItem.ShapeIndex && tempColorIndex == item.BlockItem.ColorIndex)
                {
                    sameBlockCount++;
                    if (sameBlockCount >= 3)
                    {
                        break;
                    }
                }
                else
                {
                    tempShapeIndex = item.BlockItem.ShapeIndex;
                    tempColorIndex = item.BlockItem.ColorIndex;
                    sameBlockCount = 1;
                }
            }
        }

        
        if (sameBlockCount >= 3)
        {
            var tempEmptyList = new List<int>();

            var tempSeq = DOTween.Sequence()
                .SetLink(gameObject)
                .SetUpdate(true)
                .OnComplete(() =>
                {
                    destroyEffect.Play();
                });
            // 重新进行排序
            for (var i = 0; i < basketParent.childCount; i++)
            {
                var item = basketParent.GetChild(i).GetComponent<SingleBasketItem>();
                
                if (item.HasPutBlock)
                {
                    if (item.BlockItem.ColorIndex == tempColorIndex &&
                        item.BlockItem.ShapeIndex == tempShapeIndex && sameBlockCount > 0)
                    {
                        tempEmptyList.Add(item.transform.GetSiblingIndex());

                        var tempBlock = item.BlockItem.gameObject;
                        tempSeq.Insert(0, item.BlockItem.transform.DOMove(new Vector3(0, -2.3f, 0), 0.45f))
                            .InsertCallback(0.45f, () =>
                            {
                                DestroyImmediate(tempBlock);
                            });
                        
                        item.SetBlock(null);
                        sameBlockCount--;
                    }
                    else
                    {
                        if (tempEmptyList.Count > 0)
                        {
                            tempEmptyList.Add(item.transform.GetSiblingIndex());
                            item.transform.SetSiblingIndex(tempEmptyList[0]);
                            tempEmptyList.RemoveAt(0);
                        }
                    }
                }
            }
        }
    }

CheckIsGameOver方法判断当前游戏是由还有空篮子,如果没有,则游戏失败,调用GameOver方法,具体代码如下:

private void CheckIsGameOver()
    {
        var hasEmptyBasket = false;
        foreach (var item in basketItems)
        {
            if (!item.HasPutBlock)
            {
                hasEmptyBasket = true;
                break;
            }
        }

        if (!hasEmptyBasket)
        {
            GameOver(false);
        }
    }

    private void GameOver(bool isWin)
    {
        Debug.LogError("gameOver");
        // 分别处理胜利和失败的逻辑
    }

第六步:添加时间维度,让游戏在一定时间内结束,如果时间到了,场景中还有没有消除的方块,则游戏结束;

场景中增加倒计时的展现,接下来编写代码;如下:

[SerializeField] private TextMeshProUGUI timerTmp;
    private IDisposable gameTimer;
    private void StartTimer()
    {
        var totalTime = 20;
        timerTmp.text = totalTime.ToString();
        gameTimer?.Dispose();
        gameTimer = Observable.Interval(TimeSpan.FromSeconds(1))
            .Subscribe(_ =>
            {
                totalTime--;
                timerTmp.text = totalTime.ToString();
                if (totalTime <= 0)
                {
                    GameOver(totalBlockCount <= 0);
                    gameTimer?.Dispose();
                }
            }).AddTo(this);
    }

第七步:添加判断胜利的逻辑,在上面的LoadGame方法中,会使用totalBlockCount字段记录了当前游戏一共生成了多少个方块,每进行一次消除就将数量减3,然后判断方块是否全部消除,在CheckIsGameOver方法中补充胜利的判断;,代码如下:

private void CheckIsGameOver()
    {
        if (totalBlockCount <= 0)
        {
            GameOver(true);
            return;
        }
        
        var hasEmptyBasket = false;
        foreach (var item in basketItems)
        {
            if (!item.HasPutBlock)
            {
                hasEmptyBasket = true;
                break;
            }
        }

        if (!hasEmptyBasket)
        {
            GameOver(false);
        }
    }

标签:篮子,游戏,微信,物体,private,item,Unity,小游戏,var
From: https://blog.csdn.net/lel18570471704/article/details/143417358

相关文章

  • 【Unity】微信小游戏适配之屏幕常亮
    屏幕常亮Unity:Screen.sleepTimeout=SleepTimeout.NeverSleep;微信:wx.setKeepScreenOn(Objectobject)功能描述设置是否保持常亮状态。仅在当前小程序生效,离开小程序后设置失效。参数Objectobject属性类型默认值必填说明keepScreenOnboolean 是是否保......
  • Unity中的网格与材质球合并
    很多时候我们需要把具有相同shader的材质球合并,从而减少drawcall的产生。 比如九龙战里面,一个人物带有10个部位,10个部位各自来自不同的fbx文件,加上身体,就有11个材质球,占上11个drawcall。如果主城里面跑着10个角色,光人物就占了110个drawcall!所以这种时候材质球合并是必须的。(下......
  • 掌握微信自动回复设置,让沟通更简单更高效便捷!
    你是不是也想在忙碌的时候,仍然能够及时回复好友并提高沟通效率呢?如果是的话,那么微信管理系统中的机器人功能将是你的不二之选!1、自动通过好友请求当有人向你发送好友请求时,微信管理系统能够自动审核并通过这些请求。你就不必每次都手动查看新的请求,还可以确保你不会错过任......
  • 如何用pbootcmsAPI接口开发微信小程序UNIAPP示例
    1.准备工作在开始开发小程序之前,你需要:搭建好PbootCMS环境,确保其正常运行。注册小程序并获取AppID和AppSecret。配置PbootCMS与小程序的接口。2.封装API//获取站点信息exportconstpostSite=(config={})=>http.post('/cms/site',config)//获取自定义标签ex......
  • uniapp - 详细实现移动端公众号 H5 网页授权登录流程及示例代码,申请测试公众号全流程
    前言Vue版本,请访问这篇文章。在uni-appH5网站平台开发中,详解微信公众号网页接入微信授权登录示例代码,附带申请测试公众号全流程及配置教程,提供前端h5页面公众号网页实现授权登陆并获取用户昵称头像数据的示例源码,用自己项目跑出来的本地局域网IP段就可以拉起公众......
  • 微信用不了零钱支付是什么原因
    微信用不了零钱支付可能是由多个因素引起的,包括:1.账户安全问题;2.余额不足或者超过限额;3.网络或系统故障;4.地区性限制;5.支付行为异常等。了解这些因素不仅有助于解决问题,还能提醒我们在日常使用中更加注意账户安全和合规使用。1.账户安全问题账户安全是影响微信零钱支付功能......
  • Unity性能优化-降低功耗,发热量,耗电量之OnDemandRendering篇
    公司游戏项目,手机运行严重发烫,耗电量飞快。在暂时无法做其他美术性和技术性优化的情况下,我写了这个公司内部文档,并做了个实验,今天干脆公布出来,希望对大家有用。 --官方文档:Unity-ScriptingAPI:OnDemandRendering--Youtube讲解:https://www.youtube.com/watch?v=RYgWn6jbt......
  • Unity控制物体透明度的改变
    目录标题效果图代码调用注意事项效果图代码注意:在控制全部的模型进行透视时,已经隐藏的子物体仍然要处理。usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;usingDG.Tweening;publicclassFadeModel{privateGameObj......
  • 指尖上的编程挑战:打造技术交流新桥梁——我的微信博客小程序
    在一个风和日丽的下午,我坐在窗边,看着电脑屏幕上闪烁的光标,心中涌动着一股强烈的创作欲望。作为一名热衷于技术分享的技术博主,我发现越来越多的读者希望能够随时随地阅读我的博客文章。但是,传统的博客形式似乎有些过于单一,在手机端的体验并不理想,尤其是在碎片化的时间里浏览长篇文章......
  • 国产操作系统成功安装微信
    微信目前几乎是上网必备软件,不仅在移动设备上普遍使用,也因其文件处理能力强、与移动设备端可同步,其在电脑上也普遍使用。随着国产操作系统的蓬勃发展,使用该系统的用户越来越多,但微信官网上的微信安装包没有支持国产操作系统的。经过本人的长时间搜索下载与安装试用,发现了一个可......