【制作100个unity游戏之28】花半天时间用unity复刻童年4399经典小游戏《黄金矿工》(附带项目源码)

news2024/10/7 2:23:41

最终效果

在这里插入图片描述

文章目录

  • 最终效果
  • 前言
  • 素材
  • 模拟绳子
  • 钩子来回摆动
  • 发射回收钩子方法
  • 发射钩子
  • 回收钩子
  • 勾取物品
  • 随机生成物品
  • 其他
  • 源码
  • 完结

前言

在游戏发展史上,有些游戏以其简单而耐玩的特性,深深地烙印在了玩家的记忆中。《黄金矿工》就是其中之一,它曾经在4399等游戏平台上掀起了一股游戏热潮。这款游戏不仅给无数玩家带来了欢乐,还让他们体验到了挖掘金矿的刺激与乐趣。

在这个项目中,我们将花费半天的时间,利用Unity引擎,重新打造这款经典小游戏。通过复刻《黄金矿工》,我们不仅是在向童年的回忆致敬,更是在挖掘游戏设计的精髓。在这个过程中,我们将学习如何实现游戏的核心玩法,设计简洁而有效的游戏机制。

本项目不仅仅是一个技术实践的机会,更是一个寻找游戏乐趣的旅程。无论是对于游戏开发初学者,还是对于那些怀念童年经典的老玩家,都将在这个项目中找到共鸣。希望通过这个项目,能够唤起你心中那份对于游戏的热爱与回忆,让我们一起重温那段美好的游戏时光。

素材

链接:https://pan.baidu.com/s/1bzOPsIQ3mtccH4mCm8bMzg?pwd=es5a
提取码:es5a

模拟绳子

黄金矿工游戏中,玩家通过按键操作并发射钩子来挖取金矿,钩子与绞盘之间有一条绳子进行连接,这条绳子我们使用Unity提供的LineRenderer组件来进行实现。

我们拖入钩子图片,并在上面添加LineRenderer组件,然后在Materials选项中选择默认的精灵图材质(Sprites-Default),然后将Order in Layer选项修改为1,或者降低北京图的排序,防止被背景2D物体遮挡。其中,Positions选项是用来设置线段的2点,LineRenderer组件会在游戏运行时自动在2点之间进行连线。而我们也是在代码中动态修改这2点的值,来实现绳子的效果。大致设定如图:
在这里插入图片描述
接下来,在绞盘的位置新建一个空物体,用于确定线段的起点。然后在代码中更新Positions选项中点的位置即可。代码如下:

public class Hook : MonoBehaviour {
    public Transform startTrans;    //起始点
    LineRenderer lineRenderer;
    void Start() {
        lineRenderer = GetComponent<LineRenderer>();
        lineRenderer.startWidth = 0.1f;//修改线条宽度
    }
    void Update() {
        UpdataLine();
    }
    public void UpdataLine()
    {
    	//设置线条2点的位置
        lineRenderer.SetPosition(0, startTrans.position);
        lineRenderer.SetPosition(1, transform.position);
    }
}

效果
在这里插入图片描述

钩子来回摆动

游戏中,钩子总是绕着绞盘来进行旋转。使用RotateAround这个函数就可轻松解决,难点是如何限制钩子只在下方进行旋转操作,而不会旋转到上方。此处,我们使用向量之间的夹角来进行判断,钩子是否旋转出边界。

修改Hook代码

//定义旋转方向枚举
public enum RotaDir
{
    left,
    right,
}

public RotaDir nowDir;    //玩家当前的旋转方向
public float angleSpeed;  //旋转速度
public void PlayRotate()
{

    float rightAngle = Vector3.Angle(transform.up, Vector3.right);//计算玩家前进方向与Right的夹角


    if (nowDir == RotaDir.left)
    {
        if (rightAngle < 170)
        {   //在可旋转范围内按当前方向继续旋转
            transform.RotateAround(startTrans.position, Vector3.forward, angleSpeed * Time.deltaTime);
        }
        else
        {
            nowDir = RotaDir.right;//超出范围,改变方向进行旋转
        }

    }
    else
    {
        if (rightAngle > 10)
        {
            transform.RotateAround(startTrans.position, Vector3.forward, -angleSpeed * Time.deltaTime);
        }
        else
        {
            nowDir = RotaDir.left;
        }

    }
}

效果
在这里插入图片描述

发射回收钩子方法

钩子的朝向总是自己的Up轴的反方向。那么移动的方向也是如此。只要一直朝这个方向移动就行了,返回也是同样的道理。代码如下:

public float moveSpeed;

public void PlayMoveForward()//前向移动
{
    transform.position += transform.up * -1 * moveSpeed * Time.deltaTime;
}
public void PlayBackMove()//返回移动
{
    transform.position += transform.up * moveSpeed * Time.deltaTime;
}

发射钩子

修改Hook,这里定义触摸屏幕,按s或者下按键发射钩子

public bool isFire;
public bool isBack;
public Vector3 playStartPoint;
public float moveSpeed;
public float startSpeed;

void Update()
{
    if (Input.GetKeyDown(KeyCode.S) || Input.GetKeyDown(KeyCode.DownArrow))
    {
        if (!isFire)
        {
            isFire = true;
            playStartPoint = transform.position;
        }
    }

    // 检测触摸输入
    if (Input.touchCount > 0)
    {
        Touch touch = Input.GetTouch(0); // 获取第一个触摸点

        // 如果触摸开始
        if (touch.phase == TouchPhase.Began)
        {
            if (!isFire)
            {
                isFire = true;
                playStartPoint = transform.position;
            }
        }
    }


    if (isFire && !isBack)
    {
        PlayMoveForward();
    }
    else if (isFire && isBack)
    {
        PlayBackMove();
    }


    if (!isFire)
    {
        moveSpeed = startSpeed;
        PlayRotate();
    }
    UpdataLine();
}

效果
在这里插入图片描述

回收钩子

目前如果不做限制,钩子肯定会无限发射出去,最简单的限制方法就是定义触发器边界,钩子触碰返回即可
在这里插入图片描述
修改Hook

public void PlayBackMove()//返回移动
{
    transform.position += transform.up * moveSpeed * Time.deltaTime;
    if (transform.position.y >= playStartPoint.y)
    {
    	transform.position = playStartPoint;
        isFire = false;
        isBack = false;
    }
}
    
private void OnTriggerEnter2D(Collider2D other)
{
    if (other.CompareTag("Boundary"))
    {
        isBack = true;
    }
}

效果
在这里插入图片描述

勾取物品

public GameObject item;

public void PlayBackMove()//返回移动
{
    transform.position += transform.up * moveSpeed * Time.deltaTime;
    if (transform.position.y >= playStartPoint.y)
    {
        isFire = false;
        isBack = false;
        if (item)
        {
            UIManager.Instance.SetScore(item.GetComponent<Item>().money);
            Destroy(item);
            item = null;
			
			//加钱
        }
    }
}
    
private void OnTriggerEnter2D(Collider2D other)
{
    if (other.CompareTag("Boundary"))
    {
        isBack = true;
    }

    if (other.CompareTag("Item") && item == null)
    {
        item = other.gameObject;
        isBack = true;
        item.transform.parent = transform;
        item.transform.position = transform.TransformPoint(Vector3.zero); // 将局部原点转换为世界坐标并设置位置
        item.transform.rotation = transform.rotation; // 将旋转角度设置为当前对象的旋转角度
    }
}

效果
在这里插入图片描述

随机生成物品

新增ItemSpawner,定义在spawnAreaSize区域内随机生成itemPrefabs物品

public class ItemSpawner : MonoBehaviour
{
    public static ItemSpawner Instance;
    public GameObject[] itemPrefabs; // 物品预设
    private int itemCount; // 要生成的物品数量
    public GameObject spawnAreaSize; // 生成区域大小的 GameObject

    private List<Vector3> spawnedPositions = new List<Vector3>(); // 已生成物品的位置列表

    private void Awake() {
        Instance = this;
    }

    void Start()
    {
        SpawnItems();
    }

    public void SpawnItems()
    {
        // 循环遍历子物体
        for (int i = transform.childCount - 1; i >= 0; i--)
        {
            // 获取当前子物体
            Transform child = transform.GetChild(i);

            // 销毁子物体
            Destroy(child.gameObject);
        }

        itemCount = Random.Range(5, 10);
        for (int i = 0; i < itemCount; i++)
        {
            Vector3 randomPosition = GetRandomPosition();
            GameObject itemPrefab = itemPrefabs[Random.Range(0, itemPrefabs.Length)];

            GameObject newItem = Instantiate(itemPrefab, randomPosition, Quaternion.identity);
            newItem.transform.parent = transform;
            spawnedPositions.Add(randomPosition);
        }
    }

    Vector3 GetRandomPosition()
    {
        Vector3 spawnSize = spawnAreaSize.transform.localScale;
        Vector3 randomPosition = new Vector3(Random.Range(spawnAreaSize.transform.position.x - spawnSize.x / 2, spawnAreaSize.transform.position.x + spawnSize.x / 2),
                                             Random.Range(spawnAreaSize.transform.position.y - spawnSize.y / 2, spawnAreaSize.transform.position.y + spawnSize.y / 2),
                                             Random.Range(spawnAreaSize.transform.position.z - spawnSize.z / 2, spawnAreaSize.transform.position.z + spawnSize.z / 2));

        // 检查生成的位置是否与已有的物品位置重叠
        while (CheckOverlap(randomPosition))
        {
            randomPosition = new Vector3(Random.Range(spawnAreaSize.transform.position.x - spawnSize.x / 2, spawnAreaSize.transform.position.x + spawnSize.x / 2),
                                         Random.Range(spawnAreaSize.transform.position.y - spawnSize.y / 2, spawnAreaSize.transform.position.y + spawnSize.y / 2),
                                         Random.Range(spawnAreaSize.transform.position.z - spawnSize.z / 2, spawnAreaSize.transform.position.z + spawnSize.z / 2));
        }

        return randomPosition;
    }

    bool CheckOverlap(Vector3 position)
    {
        foreach (Vector3 spawnedPos in spawnedPositions)
        {
            if (Vector3.Distance(position, spawnedPos) < 1.0f ) // 这里的1.0f是一个简单的重叠检测距离阈值
            {
                return true;
            }
        }
        return false;
    }
}

效果,每次加载都会生成不同的物品
在这里插入图片描述
在这里插入图片描述

其他

一些基本的UI,场景切换,音乐音效等等这里就不多介绍了。当然过程出现任何问题或者有更好的方法,也欢迎留下你的评论一起探讨。

源码

整理好了我会放上来的

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,最近开始自学unity,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!php是工作,unity是生活!如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1686959.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBootWeb 篇-深入了解 Mybatis 删除、新增、更新、查询的基础操作与 SQL 预编译解决 SQL 注入问题

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 Mybatis 的基础操作 2.0 基础操作 - 环境准备 3.0 基础操作 - 删除操作 3.1 SQL 预编译 3.2 SQL 预编译的优势 3.3 参数占位符 4.0 基础操作 - 新增 4.1 主键返回…

每周节省7800万工时!ChatGPT等成美国降本增效利器

5月23日&#xff0c;全球最大教育、商业出版社之一的Pearson plc在官网发布了&#xff0c;ChatGPT等生成式AI如何帮助人们提升工作效率节省时间的深度研究报告。 该报告一共分析了美国、英国、澳大利亚、巴西和印度5个国家。到2026年&#xff0c;美国节省的时间最多&#xff0…

面试准备-项目【面试准备】

面试准备-项目【面试准备】 面试准备自我介绍&#xff1a;项目介绍&#xff1a; 论坛项目功能总结简介数据库表设计注册功能登录功能显示登录信息功能发布帖子评论私信点赞功能关注功能通知搜索网站数据统计热帖排行缓存 论坛项目技术总结Http的无状态cookie和session的区别为什…

GIS竞赛指南

全国大学生GIS应用技能大赛 全国大学生GIS应用技能大赛 主办单位:中国地理信息产业协会、中国地理学会 协办单位&#xff1a;广州大学(2023年) 参赛要求&#xff1a;每个学校一支队伍&#xff0c;限在读本科生组队不超过4人&#xff0c;指导老师不超过2人&#xff0c;一般学…

三磷酸肌醇(IP3)为细胞内信号转导分子 在医药、科研领域应用前景广阔

三磷酸肌醇&#xff08;IP3&#xff09;为细胞内信号转导分子 在医药、科研领域应用前景广阔 三磷酸肌醇又称为肌醇三磷酸&#xff0c;简称InsP3或IP3&#xff0c;是一种小信号分子&#xff0c;由磷脂酶C催化磷脂酰肌醇-4,5-二磷酸水解产生的&#xff0c;水解后剩下的甘油二酯停…

异众比率(variation ratio)

异众比率&#xff08;variation ratio&#xff09;是指非众数组的频数占总频数的比率&#xff0c;其计算公式为: 其中&#xff0c;为众数组的频数。 异众比率主要用于衡量众数对一组数据的代表程度。异众比率越大&#xff0c;说明非众数组的频数占总频数的比重越大&#xff0…

甭管几岁退休,都要做纵横驰骋的疯狂老太太

点击文末“阅读原文”即可参与节目互动 剪辑、音频 / 卷圈 运营 / SandLiu 卷圈 监制 / 姝琦 封面 / 姝琦Midjourney 产品统筹 / bobo 场地支持 / &#xff08;新&#xff09;声湃轩北京录音间 对许多年轻人来说&#xff0c;退休是此生最大的“延迟满足”。 手握不用干活…

高通 Android 12/13冻结屏幕

冻结屏幕很多第一次听到以为是Android一种异常现象&#xff0c;实则不然&#xff0c;就是防止用户在做一些非法操作导致问题防止安全漏洞问题。 1、主要通过用户行为比如禁止下拉状态栏和按键以及onTouch事件拦截等&#xff0c;不知道请看这篇文章&#xff08;Touch事件传递流…

html+css绘制自定义样式输入框

效果&#xff1a; 代码&#xff1a; html部分&#xff1a; <div class"box"> <div class"newbox"><input type"text" required><div class"name">Username</div></div> </div>css部分 …

aardio - godking.vlistEx虚表点击表头全选、排序

新版虚表内置了名称为 DefaultCheckedImg 和 DefaultUnCheckedImg 的两张图片&#xff0c;分别为 【选择框勾选状态默认图片】 和 【选择框未勾选状态默认图片】 以下代码调用了这两张图片&#xff0c;所以请将虚表库升级为最新版。 如果使用旧版库&#xff0c;可以自行添加这…

数据结构面试例题:括号匹配、栈实现队列、队列实现栈,循坏队列(C语言解决)

hello,这次我来用C语言和栈和队列相关问题&#xff0c;希望大家多多支持 括号匹配题目&#xff1a; OJ题目 首先我们不能用数量匹配来进行解答&#xff0c;因为你顺序不一定匹配 解析&#xff1a; 1、左括号入栈 2、右括号出栈顶左括号匹配 1.我们只需要找到右括号&#xff0c…

昔日辉煌不再,PHP老矣,尚能饭否?

导语 | 近期 TIOBE 最新指数显示&#xff0c;PHP 的流行度降至了历史最低&#xff0c;排在第 17 名&#xff0c;同时&#xff0c;在年度 Stack Overflow 开发者调查报告中&#xff0c;PHP 在开发者中的受欢迎程度已经从之前的约 30% 萎缩至现在的 18%。“PHP 是世界上最好的语言…

每日5题Day9 - LeetCode 41 - 45

每一步向前都是向自己的梦想更近一步&#xff0c;坚持不懈&#xff0c;勇往直前&#xff01; 第一题&#xff1a;41. 缺失的第一个正数 - 力扣&#xff08;LeetCode&#xff09; 今天这道题没有ac&#xff0c;写不动了&#xff0c;下次再通过吧&#xff0c;先给个半成品下次回…

装备制造项目管理软件:奥博思PowerProject项目管理系统

数字化正逐步改变着制造方式和企业组织模式。某制造企业领导层透露&#xff0c;在采用数字化项目管理模式后&#xff0c;企业的发展韧性更加强劲&#xff0c;构筑起了竞争新优势&#xff0c;企业产品研制周期缩短25%&#xff0c;生产效率提升18%。 随着全球经济的发展&#xf…

【Linux001】centos常用命令总结总结(已更新)

1.熟悉、梳理、总结下centos知识体系。 2.Linux相关知识&#xff0c;在日常开发中必不可少&#xff0c;如一些必知必会的常用命令&#xff0c;如环境搭建、应用部署等。同时&#xff0c;也要谨慎使用一些命令&#xff0c;如rm -rf&#xff0c;防止一些生产事故的发生。 3.欢迎点…

521源码-区块链交易所-二开版多语言秒合约交易所系统/区块链交易所系统/完整脚本任务/附带搭建教程

交易只保留秒合约交易&#xff0c;完整的计划执行脚本&#xff0c;秒合约订单完美结算&#xff0c;效果看图片 搭建非常简单&#xff0c;功能不多叙述&#xff0c;自行测试吧 本源码下载地址&#xff1a;二开版多语言秒合约交易所系统/区块链交易所系统/完整脚本任务/附带搭建…

抖音小店没有流量不出单?归根到底,就是转化率不行!

哈喽~我是电商月月 新手做抖音小店&#xff0c;最忧愁的就是&#xff1a;店铺不出单怎么办&#xff1f; 商家通常会把没有销量的原因&#xff0c;都推向于“店铺没有流量” 但在抖音&#xff0c;这个日活量高达9亿的平台来说&#xff0c;任何商铺最不缺的应该就是流量了 但…

如何异地组网添加摄像机?

本文将介绍如何使用天联技术实现异地组网添加摄像机&#xff0c;并保障数据的安全性。 安防摄像机的应用愈发广泛&#xff0c;无论是家庭安防还是企业监控&#xff0c;摄像机都扮演着重要角色。在一些特殊场合或者特殊需求下&#xff0c;我们需要将摄像机添加到异地网络中进行监…

人工智能与区块链技术:开启未来科技的双引擎

在当今科技飞速发展的时代&#xff0c;人工智能和区块链技术如同两颗璀璨的明星&#xff0c;照亮了人类通往未来的道路。 人工智能&#xff0c;以其强大的学习和分析能力&#xff0c;正悄然改变着我们的生活。它能够处理海量的数据&#xff0c;为我们提供精准的预测和个性化的…