猫史档案馆


【新版怪物跟踪代码】加入射线检测,判断更加合理。

用户:LWKevin0wvf小号LWKevin0wvf小号查看:4 回复:9 评论:4 创建时间:2024-05-06T22:18:36


射线检测判断更合理,不会穿墙看到玩家,能够自动跳跃(Demo版自动跳跃,有时检测不灵)

开源协议:MPL-2.0 https://opensource.org/license/mpl-2-0 https://www.mozilla.org/en-US/MPL/2.0/

请遵循开源协议,严禁侵权。

对源代码进行修改后的代码必须以同样或相似的开源协议开源。

monsters.js文件(文件名改为monsters)

const MeshScale = new GameVector3(1 / 16, 1 / 16, 1 / 16);
const Quat = new GameQuaternion(0, 0, 0, 1);
module.exports = {
    normalZombie: {
        /**
         * @description 生成
         * @param {number} x x坐标
         * @param {number} y y坐标
         * @param {number} z z坐标
         * @param {GameVector3} scale 大小
         */
        spawn(x, y, z, scale = MeshScale) {
            var e = world.createEntity({
                id: "normalZombie",
                position: new GameVector3(x, y, z),
                collides: true,
                gravity: true,
                mesh: "mesh/normalZombie.vb",
                meshScale: scale
            });
            e.addTag("monster");
            Object.assign(e, {
                sightingDistance: 32,//视野距离
                speed: 0.5,//速度
                jumpPower: 1,//跳跃高度
                funcs: [],
                //每隔500毫秒(0.5秒)执行函数
                interval: setInterval(async () => {
                    for (var func of e.funcs) func(e);//执行函数传入丧尸实体参数e
                }, 100),
                onInterval(...funcs) {
                    for (var func of funcs) this.funcs.push(func);
                }
            });
            e.onDie(() => {
                clearInterval(e.interval);//结束间隔执行函数
                e.destroy();
            });
            //用setInterval仅仅是因为我不想用onTick)))
            e.onInterval((e) => {
                //寻找target(不能穿墙看到玩家)
                this.findTarget(e);//寻找目标玩家实体
                if (e.isPlayer) console.log(e.player.name)
                if (e.target && !e.target.destroyed && e.target.hp) {
                    this.move(e, e.target.position);//移动一步(?)
                    //如果靠近玩家进行攻击(播放攻击动画、扣防御值/血机制,玩家受伤后玩家要执行的代码写在index里的entity.onTakeDamage里)
                }
            });
            return e;
        },
        /**
         * @description 移动,调用一次只移动一步
         * @param {GameEntity} e 丧尸实体
         * @param {GameVector3} targetPosition 目标位置
         */
        move(e, targetPosition) {
            var direction = targetPosition.sub(e.position);
            var dist = direction.mag();
            e.velocity.x = direction.x * e.speed / dist;
            e.velocity.z = direction.z * e.speed / dist;
            //自动上楼梯机制(有时不灵敏)
            //先用射线判断前面1格距离里有没有方块,再判断上一格有没有固体方块,如果能跳上去就跳跃。(跳跃高度默认设定1格,这里有点乱,jumpPower是跳跃力度,不是跳跃高度,但是我没有跳跃高度计算公式。)
            //onVoxelContact不好用
            //发射一条起点在腿位置的射线
            //new GameVector3(e.velocity.x, 0, e.velocity.y)
            var raycast = world.raycast(e.position.add(new GameVector3(0, -0.5, 0)), getVectorByOrientation(e.meshOrientation), {
                maxDistance: 2,//最大距离
                ignoreFluid: true,//忽略液体
                ignoreSelector: ".monster"//忽略怪物
            });
            //如果击中方块 或 (击中实体 且 实体不是玩家 且 实体开碰撞了 且 实体能跳上去)
            if (raycast.hitVoxel ||
                (raycast.hitEntity && !raycast.hitEntity.isPlayer && raycast.hitEntity.collides && (
                    (raycast.hitEntity.position.y + (raycast.hitEntity.bounds.y / 2)
                        - e.position.y) <= e.jumpPower))) {
                console.log(1);
                //再发射一条起点在眼睛位置的射线
                var raycast2 = world.raycast(e.position.add(new GameVector3(0, 0.5, 0)), getVectorByOrientation(e.meshOrientation), {
                    maxDistance: 2,//最大距离
                    ignoreFluid: true,//忽略液体
                    ignoreSelector: "*"//忽略怪物
                });
                //如果没击中方块 或 (击中的实体 是玩家 或 没开碰撞),就可以跳跃
                if (!raycast2.hitVoxel ||
                    (raycast2.hitEntity && (raycast2.hitEntity.isPlayer || !raycast2.hitEntity.collides)))
                    e.velocity.y = e.jumpPower;
            }
            var orientation = Quat.rotateY(Math.atan2(e.velocity.z, e.velocity.x));
            e.meshOrientation.copy(orientation);
        },
        /**
         * @description 寻找目标
         * @param {GameEntity} e 丧尸实体
         * @returns {GamePlayerEntity} 目标玩家实体
         */
        findTarget(e) {
            /*
            1.不能穿墙看到玩家(能穿墙就直接找distance最小,不用射线)
            2.超过视线距离就看不到
            如果找不到就把e.target设为undefined
            功能:给e.target赋值为目标玩家实体并返回e.target
            */
            //擂台法找最近且能看到
            e.target = undefined;//先赋值为undefined,防止找不到时返回之前的已经看不到的目标。
            for (var player of world.querySelectorAll("player")) {
                var distance = player.position.distance(e.position);
                //如果丧尸还没有目标实体 或 距离比喵来目标实体的距离更近
                if ((!e.target || distance < e.target.position.distance(e.position))) {
                    //判断能否看到(先求出方向,再发喵线)
                    var direction = player.position.sub(e.position);
                    //射线起点要稍高于丧尸位置才是丧尸眼睛位置
                    var raycast = world.raycast(e.position.add(new GameVector3(0, 0.5, 0)), direction, {
                        maxDistance: e.sightingDistance,//视野距离
                        ignoreFluid: true,//忽略液体
                        ignoreSelector: ".monster"//忽略其他怪物
                    });
                    if (raycast.hitEntity === player) e.target = player;//找到目标!!
                }
            }
            return e.target;
        }
    }
}
/**
 * @description 把四元数转成旋转向量
 * @param {GameQuaternion} orientation 四元数
 * @returns {GameVector3} 旋转向量
 * 四元数与旋转向量的转化 见https://www.cnblogs.com/narjaja/p/16358263.html
 */
function getVectorByOrientation(orientation) {
    var theta = 2 * Math.acos(orientation.w);
    return new GameVector3(
        orientation.x / Math.sin(theta * 0.5) * theta,
        orientation.y / Math.sin(theta * 0.5) * theta,
        orientation.z / Math.sin(theta * 0.5) * theta);
}

index.js文件

"use strict";
console.clear();
const { normalZombie } = require("./monsters.js");
normalZombie.spawn(0,11,0);//在坐标{x:0,y:11,z:0}的地方生成普通僵尸

 


回复

上一页1 页 / 共 1下一页
LWKevin0wvf小号LWKevin0wvf小号

ddd

点赞0


评论


追梦ez追梦ez

nbnb

点赞1


评论


追梦ez追梦ez

ddd

点赞0


评论


LWKevin0wvf小号LWKevin0wvf小号

opensource.org/license/mpl-2-0

点赞0


评论


LWKevin0wvf小号LWKevin0wvf小号

ddd

点赞0


评论


LWKevin0wvf小号LWKevin0wvf小号

ddd

点赞0


评论


爱过猫站爱过猫站

你好啊

点赞0


评论


LWKevin0wvf小号LWKevin0wvf小号

ddd

点赞0


评论


LWKevin0wvf小号LWKevin0wvf小号

更新的版本:shequ.cod喵/community/695045

点赞0


评论