为了吃鸡苦练狙击,避免坑队友自己造一个狙击游戏!

news2024/12/23 19:27:35

在这里插入图片描述

引言

一文教会你造一个简易的狙击游戏。

说到狙击,相信大家都不陌生,无论是影视作品还是网络游戏,都经常能看到狙击枪的身影,最深刻的是它能够从百里之外,一枪爆头

本文将介绍如何在Cocos Creator中造一个简易的狙击游戏非常详细

本文源工程在文末获取,小伙伴们自行前往。

1.狙击游戏常见的元素有什么?

以下是狙击游戏中常见的一些元素

  1. 狙击枪: 各种类型的狙击枪。

  2. 瞄准镜: 玩家可以使用各种瞄准镜来提高精准度和观察敌人。

  3. 目标: 狙击目标。

那么实现狙击游戏需要哪些知识点?

2.实现狙击游戏的知识点

想要在Cocos Creator中造一个简易的狙击游戏,需要掌握以下知识点:

  1. 动画编辑:狙击游戏通常包含一些动画效果,例如打开瞄准镜的时候、子弹的运动轨迹等等都需要一些简易的动画效果,本文用到动画编辑器Tween动画。

  2. 瞄准镜效果:瞄准镜的效果通常使用RTT方法,全称RenderToTexture,通过把摄像机拍到的内容渲染到2DUI上。

  3. 碰撞检测:本文是简易狙击游戏,开枪即判断是否命中,采用的是屏幕射线检测,子弹的物理碰撞不做详细介绍。

  4. 圆形遮罩:圆形的瞄准镜,需要借助一下Mask遮罩。

  5. 3D游戏基础:例如模型的摆放、坐标的计算转换以及相机的调整

以上相关知识点笔者前面的文章都有介绍,可在文末100个Cocos实例专栏查阅。

话不多说,一起来看下如何在Cocos Creator中造一个简易的狙击游戏

3.一起来造一个简易的狙击游戏

我们根据以下的步骤一步一步来造一个简易的狙击游戏:

1.环境

引擎版本:Cocos Creator 3.8.1

编程语言:TypeScript

2.资源准备

首先从市场搞一把帅气带瞄准镜狙击枪模型,还送了个开火特效!(这回节目组可是下重本啊。)

下重本了

然后找一张简单的瞄准镜画面UI,准备做几个小按钮用作瞄准镜开镜、射击和重置复位。

与技术无关,没有他我能闭着眼爆头

给瞄准镜添加一个Mask组件形成圆形遮罩。

方便好用

为了营造一个非常好的打鸡效果,我们把熟悉的鸡朋友拉过来当靶子

给我摆高一点

添加2个摄像机,并且分别调整各自的摄像机的机位,包括原有的主摄像机瞄准镜摄像机子弹轨迹跟踪摄像机

没想到做个Demo都那么不容易

小技巧

瞄准镜摄像机可以和主摄像机一致,包括位置、旋转和设置,通过改变相机Fov实现放大效果

通过动画编辑器简单编辑一下开镜动画

有手就行

3.编写代码

首先定义一个Snipe组件,包含以下几个属性。

@ccclass('Snipe')
export class Snipe extends Component {
    bulletPfb: Node;         //子弹预制体
    animation: Animation;    //动画组件
    sighting: Node = null;   //瞄准UI节点
    bullet: Node;            //当前子弹
    checkerCameraNode: Node; //相机检测节点
}

然后在start方法里面初始化一下,并且监听一下开镜、射击、重置事件。

start() {
    this.animation = this.node.getComponent(Animation);
    director.getScene().on("PreShoot", this.PreShoot, this);
    director.getScene().on("Shoot", this.Shoot, this);
    director.getScene().on("Reset", this.Reset, this);
    this.bulletPfb = this.node.getChildByPath("qiang/Line16");
    this.SightingCamera();
}

开镜、射击、重置事件从UI_Joystick中的按钮发出。

const sighting = this.node.getChildByName('Sighting');
this.node.getChildByName('BtnOpen').on(NodeEventType.TOUCH_END, () => {
    this._scene.emit("PreShoot", sighting);
}, this);
this.node.getChildByName('BtnShoot').on(NodeEventType.TOUCH_END, () => {
    if (sighting.active) {
        this._scene.emit("Shoot", checkerCamera.node);
    }
}, this);
this.node.getChildByName('BtnReset').on(NodeEventType.TOUCH_END, () => {
    this._scene.emit("Reset");
}, this);

实现瞄准镜的核心源码

  • 创建RenderTexture
  • 设置摄像机的targetTexture为上面创建的RenderTexture
  • 创建SpriteFrame也设置它的texture为上面创建的RenderTexture
  • 最后将瞄准镜SpritespriteFrame为上面创建的SpriteFrame
SightingCamera() {
    const modelRtt = new RenderTexture();
    modelRtt.reset({
        width: 1024,
        height: 1024
    });
    const camera = find("Main Camera/SightingCamera").getComponent(Camera);
    camera.targetTexture = modelRtt;
    const spriteFrame = new SpriteFrame();
    spriteFrame.texture = modelRtt;
    find("Canvas/ui_joystick_panel/Sighting/Mask/SightingSprite").getComponent(Sprite).spriteFrame = spriteFrame;
}

PreShoot方法中实现开镜动画的播放,核心API如下。

  • 通过animation.play播放动画。
  • 通过animation.on(Animation.EventType.FINISHED监听动画播放完成。
  • 通过animation.targetOff取消监听。
PreShoot(sighting: Node, callback = null) {
    if (this.bullet) return;
    this.sighting = sighting;
    if (this.node.children[0].active) {
        this.animation.targetOff(this);
        this.animation.on(Animation.EventType.FINISHED, (event) => {
            this.sighting.active = true;
            this.node.children[0].active = false;
        }, this);
        this.animation.play("animation");
    }
    else {
        this.animation.targetOff(this);
        if (callback) {
            this.animation.on(Animation.EventType.FINISHED, (event) => {
                callback();
            }, this);
        }
        this.sighting.active = false;
        this.node.children[0].active = true;
        this.animation.play("animation2");
    }
}

Shoot方法中利用射线检测判断瞄准镜是否瞄准了目标核心API如下。

  • 通过camera.screenPointToRay 产生射线。
  • 通过PhysicsSystem.instance.raycast 进行射线碰撞检测并记录结果。
  • 通过PhysicsSystem.instance.raycastResults 获取射线检测结果,通过名字或者其他信息得到想要的物体。
Shoot(checkerCameraNode: Node) {
    this.checkerCameraNode = checkerCameraNode;
    var ray = new geometry.Ray();
    var camera = find("Main Camera").getComponent(Camera);
    var size = view.getViewportRect();
    camera.screenPointToRay(size.width / 2, size.height / 2, ray);
    if (PhysicsSystem.instance.raycast(ray)) {
        const raycastResults = PhysicsSystem.instance.raycastResults;
        for (let i = 0; i < raycastResults.length; i++) {
            const item = raycastResults[i];
            if (item.collider.node.name == "rooster_man_skin") {
                this.OnShootTarget(item.collider.node, item.hitPoint);
                return;
            }
        }
        this.OnShoot();
    } else {
        this.OnShoot();
    }
}

最后在OnShootTarget中通过Tween动画运行子弹并且击中目标。

OnShootTarget(hitNode: Node, hitPoint: Vec3) {
    this.checkerCameraNode.active = false;
    this.PreShoot(this.sighting, () => {
        this.node.children[1].active = true;
        const bullet = instantiate(this.bulletPfb);
        this.bullet = bullet;
        bullet.parent = this.bulletPfb.parent;
        bullet.children[0].active = true;
        // tween(bullet.children[1]).by(0.5, { eulerAngles: new Vec3(0, 360, 0) }).repeatForever().start();
        tween(bullet).by(3, { position: new Vec3(0, -0.5, 0) }).to(1, { worldPosition: hitPoint }).call(() => {
            bullet.getComponentInChildren(MeshRenderer).enabled = false;
            hitNode.getComponent(CharacterMovement).onJump("btn_slot_0");
            this.node.children[1].active = false;

        }).start();
        tween(bullet.children[0].getComponent(Camera)).to(3, { fov: 30 }).to(1, { fov: 80 }).start();
    })
}

4.效果演示

瞄准镜动画效果。

在这里插入图片描述

瞄准镜效果。

在这里插入图片描述

射击效果。
在这里插入图片描述

整体效果。

在这里插入图片描述

结语

本文源工程可通过阅读原文或者私信发送"Snipe"付费获取。付费不仅是知识的获取,更是对笔者的支持和认可,感谢!

我是"亿元程序员",一位有着8年游戏行业经验的主程。在游戏开发中,希望能给到您帮助, 也希望通过您能帮助到大家。

AD:笔者线上的小游戏《贪吃蛇掌机经典》《重力迷宫球》《填色之旅》大家可以自行点击搜索体验。

实不相瞒,想要个在看!请把该文章分享给你觉得有需要的其他小伙伴。谢谢!

推荐专栏:

100个Cocos实例

8年主程手把手打造Cocos独立游戏开发框架

和8年游戏主程一起学习设计模式

游戏开发的技巧、心得、资讯

从零开始开发贪吃蛇小游戏到上线系列

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

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

相关文章

Jenkins Pipeline 脚本优化实践:从繁琐到简洁

引言 在持续集成的过程中&#xff0c;Jenkins Pipeline 是非常关键的一环。它定义了如何自动编译、测试和部署代码。随着项目的不断发展&#xff0c;Pipeline 的复杂性也在不断上升&#xff0c;这就需要我们持续优化 Pipeline 脚本&#xff0c;以提高代码的可读性和维护性。本…

最好的猫粮排行榜前十名有哪些牌子?盘点好的主食冻干猫粮前五名牌子

现在很多猫咪因为吃了不好的猫粮&#xff0c;出现了各种问题&#xff0c;甚至有的还发生了悲剧&#xff0c;让猫主人心疼又无奈。要解决这个问题&#xff0c;选择一款健康又安全的主食冻干猫粮是非常关键的。优质的主食冻干猫粮不仅在配方上要健康&#xff0c;营养配比也要科学…

【深度学习】注意力机制(二)

本文介绍一些注意力机制的实现&#xff0c;包括EA/MHSA/SK/DA/EPSA。 【深度学习】注意力机制&#xff08;一&#xff09; 【深度学习】注意力机制&#xff08;三&#xff09; 目录 一、EA&#xff08;External Attention&#xff09; 二、Multi Head Self Attention 三、…

26 redis 中 replication/cluster 集群中的主从复制

前言 我们这里首先来看 redis 这边实现比较复杂的 replication集群模式 我们这里主要关注的是 redis 这边的主从同步的相关实现 这边相对比较简单, 我们直接基于 cluster集群模式 进行调试 主从命令同步复制 比如这里 master 是 redis_7002, slave 是 redis_7005 然后 这…

打开软木塞,我们来谈谈葡萄酒泡泡吧

香槟是任何庆祝场合的最佳搭配。从婚礼和生日到单身派对和典型的周五晚上&#xff0c;这款气泡饮料是生活中特别聚会的受欢迎伴侣。 来自云仓酒庄品牌雷盛红酒分享你知道吗&#xff0c;你喜欢喝的那瓶香槟酒可能根本不是香槟&#xff0c;而是汽酒&#xff1f;你不是唯一一个认…

6个超好用的小众图片素材网站,高清、免费,值得收藏~

推荐几个超好用的图片素材网站&#xff0c;免费下载&#xff0c;还可以商用&#xff0c;建议收藏哦~ 1、菜鸟图库 https://www.sucai999.com/pic.html?vNTYwNDUx 我推荐过很多次的设计素材网站&#xff0c;除了设计类素材&#xff0c;还有很多自媒体可以用到的高清图片、背景…

最好的猫粮排行榜前十名有哪些品牌?质量好的主食冻干猫粮分享

为什么越来越多人推荐冻干猫粮喂养呢&#xff1f;主食冻干猫粮究竟是最适应猫饮食习惯的喂养方式还是消费陷阱&#xff1f; 作为一个6年的宠物营养师&#xff0c;我以前接触过很多不同品种的猫咪&#xff0c;一只健康又漂亮的猫咪从表面上就能看出来&#xff01;体型匀称刚好、…

大模型落地,向量数据库到底能做什么?

▼最近直播超级多&#xff0c;预约保你有收获 今晚直播&#xff1a;《AI编程向量数据库架构设计案例实践》 —1— 大模型的“数据局限性” 数据局限对企业做 LLM 大模型带来的影响&#xff0c;可归结为以下三点&#xff1a; 第一点&#xff1a;对数据的管理和运维。随着文本、…

LeetCode(63)旋转链表【链表】【中等】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 旋转链表 1.题目 给你一个链表的头节点 head &#xff0c;旋转链表&#xff0c;将链表每个节点向右移动 k 个位置。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], k 2 输出&#xff1a;[4,5,1,2,3]示例 2&…

深入理解LightGBM

1. LightGBM简介 GBDT (Gradient Boosting Decision Tree) 是机器学习中一个长盛不衰的模型&#xff0c;其主要思想是利用弱分类器&#xff08;决策树&#xff09;迭代训练以得到最优模型&#xff0c;该模型具有训练效果好、不易过拟合等优点。GBDT不仅在工业界应用广泛&#…

python初试二

连接数据库 Django为多种数据库后台提供了统一的调用API。根据需求不同&#xff0c;Django可以选择不同的数据库后台。MySQL算是最常用的数据库。我们这里将Django和MySQL连接。 在Linux终端下启动mysql: $mysql -u root -p 在MySQL中创立Django项目的数据库&#xff1a; …

【数据结构和算法】判断子序列

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、题目描述 二、题解 2.1 方法一&#xff1a;双指针 三、代码 3.1 方法一&#xff1a;双指针 3.1.1 Java易懂版&#xff1a;…

Cosmopolitan Libc:让 C 语言一次构建、随处运行 | 开源日报 No.109

jart/cosmopolitan Stars: 12.9k License: ISC Cosmopolitan Libc 使 C 成为一种构建一次运行在任何地方的语言&#xff0c;就像 Java 一样&#xff0c;但它不需要解释器或虚拟机。相反&#xff0c;它重新配置了标准 GCC 和 Clang 以输出符合 POSIX 标准的多语言格式&#xff…

VS Code连接远程Linux服务器调试C程序

1.在 VS Code 上安装扩展 C/C 2.通过 VS Code 连接远程 Linux 服务器 3.通过 VS Code 在远程 Linux 服务器上安装扩展 C/C 4.打开远程 Linux 服务器上的文件夹 【注】本文以 /root/ 为例。 5.创建项目文件夹&#xff0c;并在项目文件夹下创建C程序 6.按 F5&#xff0c;选…

数据挖掘任务一般流程

数据挖掘是从大量数据中提取有价值信息的过程。它涉及多个步骤&#xff0c;每一步都对整个数据挖掘过程至关重要。以下是数据挖掘任务的一般流程&#xff1a; 业务理解&#xff1a; 确定业务目标。评估当前情况。定义数据挖掘问题。制定一个初步计划来达到这些目标。 数据理…

JVM的类的生命周期

目录 前言 1. 加载&#xff08;Loading&#xff09;&#xff1a; 2. 验证&#xff08;Verification&#xff09;&#xff1a; 3. 准备&#xff08;Preparation&#xff09;&#xff1a; 4. 解析&#xff08;Resolution&#xff09;&#xff1a; 5. 初始化&#xff08;Ini…

解决ES伪慢查询

一、问题现象 服务现象 服务接口的TP99性能降低 ES现象 YGC&#xff1a;耗时极其不正常, 峰值200次&#xff0c;耗时7sFULL GC&#xff1a;不正常,次数为1但是频繁&#xff0c;STW 5s慢查询&#xff1a;存在慢查询5 二 解决过程 1、去除干扰因素 从现象上看应用是由于某种…

从零开始:前端架构师的基础建设和架构设计之路

文章目录 一、引言二、前端架构师的职责三、基础建设四、架构设计思想五、总结《前端架构师&#xff1a;基础建设与架构设计思想》编辑推荐内容简介作者简介目录获取方式 一、引言 在现代软件开发中&#xff0c;前端开发已经成为了一个不可或缺的部分。随着互联网的普及和移动…

算法通关第十九关-青铜挑战理解动态规划

大家好我是苏麟 , 今天聊聊动态规划 . 动态规划是最热门、最重要的算法思想之一&#xff0c;在面试中大量出现&#xff0c;而且题目整体都偏难一些对于大部人来说&#xff0c;最大的问题是不知道动态规划到底是怎么回事。很多人看教程等&#xff0c;都被里面的状态子问题、状态…

文章解读与仿真程序复现思路——电力系统自动化EI\CSCD\北大核心《考虑电力-交通交互的配电网故障下电动汽车充电演化特性》

这个标题涉及到电力系统、交通系统和电动汽车充电的复杂主题。让我们逐步解读&#xff1a; 考虑电力-交通交互的配电网故障&#xff1a; 电力-交通交互&#xff1a; 指的是电力系统和交通系统之间相互影响、相互关联的关系。这可能涉及到电力需求对交通流量的影响&#xff0c;反…