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

引言

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

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

本文将介绍如何在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个 摄像机,并且分别调整各自的摄像机的机位 ,包括原有的主摄像机瞄准镜摄像机子弹轨迹跟踪摄像机

小技巧

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

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

3.编写代码

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

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

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

Typescript 复制代码
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中的按钮发出。

Typescript 复制代码
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
Typescript 复制代码
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取消监听。
Typescript 复制代码
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 获取射线检测结果,通过名字或者其他信息得到想要的物体。
Typescript 复制代码
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动画运行子弹并且击中目标。

Typescript 复制代码
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年游戏主程一起学习设计模式

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

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

相关推荐
栈老师不回家41 分钟前
Vue 计算属性和监听器
前端·javascript·vue.js
前端啊龙1 小时前
用vue3封装丶高仿element-plus里面的日期联级选择器,日期选择器
前端·javascript·vue.js
一颗松鼠1 小时前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript
小远yyds1 小时前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
吕彬-前端2 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱2 小时前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai2 小时前
uniapp
前端·javascript·vue.js·uni-app
帅比九日3 小时前
【HarmonyOS Next】封装一个网络请求模块
前端·harmonyos
bysking3 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓3 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js