分享一个绘制扇形网格组件

昨天到有同学发贴需要攻击范围提示效果,分享一个扇形网格生成组件,其它形状同理,自己写组装算代码即可。

import { Component, gfx, macro, Material, MeshRenderer, utils, Vec3, _decorator } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('DrawSectorMesh')
/** 
 * 绘制扇形网格
 */
export class DrawSectorMesh extends Component {
    @property({ type: Material })
    public mat: Material | null = null;

    @property({
        tooltip: "外圈半径"
    })
    public radius: number = 5;

    @property({
        tooltip: "内圈半径"
    })
    public innerRadius: number = 1;

    @property({
        tooltip: "扇形角度"
    })
    public angledegree: number = 60;

    start() {
        this.createMesh()
    }

    createMesh() {
        const model = this.addComponent(MeshRenderer)!;
        const segments: number = Math.floor(this.angledegree / 4) + 1;                 

        var positions: number[] = [];                                                    

        // 组装顶点数据
        var vertices_count: number = segments * 2 + 2;                                    
        var vertices: Array<Vec3> = new Array<Vec3>(vertices_count);
        var angleRad: number = this.angledegree * macro.RAD;                           
        var angleCur: number = angleRad;
        var angledelta: number = angleRad / segments;                                   
        for (var i = 0; i < vertices_count; i += 2) {                                     
            var cosA: number = Math.cos(angleCur);
            var sinA: number = Math.sin(angleCur);

            vertices[i] = new Vec3(this.radius * cosA, 0, this.radius * sinA);              
            vertices[i + 1] = new Vec3(this.innerRadius * cosA, 0, this.innerRadius * sinA); 
            angleCur -= angledelta;

            positions.push(vertices[i].x);
            positions.push(vertices[i].y);
            positions.push(vertices[i].z);
            positions.push(vertices[i + 1].x);
            positions.push(vertices[i + 1].y);
            positions.push(vertices[i + 1].z);
        }


        // 组装三角形数据
        var indice_count: number = segments * 6;                                               
        var indices: Array<number> = new Array<number>(indice_count);
        for (var i = 0, vi = 0; i < indice_count; i += 6, vi += 2) {                           
            indices[i] = vi;
            indices[i + 1] = vi + 3;
            indices[i + 2] = vi + 1;
            indices[i + 3] = vi + 2;
            indices[i + 4] = vi + 3;
            indices[i + 5] = vi;
        }

        // 组装UV数据
        var uvs: number[] = [];
        for (var i = 0; i < vertices_count; i++) {
            var u = vertices[i].x / this.radius / 2 + 0.5
            var v = vertices[i].z / this.radius / 2 + 0.5
            uvs.push(u, v);
        }

        const primitiveMode = gfx.PrimitiveMode.TRIANGLE_FAN;
        const attributes: any[] = [{
            name: gfx.AttributeName.ATTR_NORMAL,
            format: gfx.Format.RGB32F,
        }];

        var IGeometry = {
            positions: positions,
            indices: indices,
            uvs: uvs,
            primitiveMode: primitiveMode,
            attributes: attributes
        }

        const mesh = utils.createMesh(IGeometry);
        model.mesh = mesh;
        model.material = this.mat;
    }
}
7赞

谢谢,谢谢我研究一下

让美术建一个模型不是更轻松一些么

这个绘制要好很多。可以测试的时候绘制,真正运行的时候 不再绘制。而且半径弧度这些都要弄。都要一一对应还是比较麻烦的,用mesh比较方便,

大佬有个问题,现在我用你的代码直接绘制出来了对应的扇形,但是这个扇形在角色侧面不是 不是我想要的角度,我应该如何去修改这个弧度。我看你在代码里面没有有设置这个角度啊。

我这里只是给你规制网格的代码,角度是另外一个控制这个模型所在节点的旋转的。要你自己写

有些需求需要动态调整模型,比如通过升级加攻击范围等,美术一般会说,你要不做个动态的,我给你个贴图自己适配。 固定需求第一选择还是美术做 :rofl:

嗯嗯,好的。我不明白还有一点就是,为啥创建完顶点跟三角形后,还要去组装uv的数据,我查阅unity的资料发现只需要组装顶点跟三角形就好了。
代码如下

GameObject go;
MeshFilter mf;
MeshRenderer mr;
Shader shader;

// 绘制实心扇形
public void ToDrawSectorSolid(Transform t, Vector3 center, float angle, float radius)
{
    int pointAmmount = 100;
    float eachAngle = angle / pointAmmount;

    Vector3 forward = t.forward;
    List<Vector3> vertices = new List<Vector3>();

    vertices.Add(center);
    for (int i = 0; i < pointAmmount; i++)
    {
        Vector3 pos = Quaternion.Euler(0f, -angle / 2 + eachAngle * (i - 1), 0f) * forward * radius + center;
        vertices.Add(pos);
    }
    CreateMesh(vertices);
}

// 创建网格
private GameObject CreateMesh(List<Vector3> vertices)
{
    int[] triangles;
    Mesh mesh = new Mesh();

    int triangleAmount = vertices.Count - 2;
    triangles = new int[3 * triangleAmount];

    // 根据三角形的个数,来计算绘制三角形的顶点顺序
    for (int i = 0; i < triangleAmount; i++)
    {
        triangles[3 * i] = 0;
        triangles[3 * i + 1] = i + 1;
        triangles[3 * i + 2] = i + 2;
    }

    if (go == null)
    {
        go = new GameObject("mesh");
        go.transform.position = new Vector3(0f, 0.1f, 0.5f);

        mf = go.AddComponent<MeshFilter>();
        mr = go.AddComponent<MeshRenderer>();

        shader = Shader.Find("Unlit/Color");
    }

    mesh.vertices = vertices.ToArray();
    mesh.triangles = triangles;

    mf.mesh = mesh;
    mr.material.shader = shader;
    mr.material.color = Color.red;

    return go;
}

我这里是通过摇杆控制角度,在设置到扇形的欧拉角上(Y轴),可以自由旋转,看你需求是什么样
image

无UV效果
image

有UV效果
image

好的多谢大佬,我大致明白了。谢谢

1赞

分享计算攻击区域的两个方法。(注意我的模型的正方向是Right)
//获取敌人是是否在扇形区域内

getSectorArea(){

    //与敌人的距离

    let distance = Vec3.distance(this.node.getWorldPosition(),this.enemy.getWorldPosition());

    //玩家正前方的向量

    let out = v3(0,0,0);

    Vec3.transformQuat(out,Vec3.RIGHT,this.node.rotation);

    // console.log(out)

    //玩家与敌人的方向向量

    let temVec = v3(0,0,0);

    Vec3.subtract(temVec,this.enemy.getWorldPosition(),this.node.getWorldPosition());

    let angle =  misc.radiansToDegrees(Vec3.angle(out,temVec));

    // console.log(angle);

    if(distance < this.proPerty.roleAtkRadiu){

        if(angle<this.attackAngle*0.5){

            console.log("小球出现在扇形范围内!!!!!");

            console.log("Vec.Right"+Vec3.RIGHT +"Vec.forward" + v3(0,0,1))

        }

    }

}

//获取敌人是否在矩形范围内

getRectangleSolid(){

    //玩家与敌人的方向向量

    let temVec = v3(0,0,0);

    Vec3.subtract(temVec,this.enemy.getWorldPosition(),this.node.getWorldPosition());

    //这里要注意模型的Right为正前方

    //与玩家正前方做点积

    //玩家正前方的向量

    let out = v3(0,0,0);

    Vec3.transformQuat(out,Vec3.RIGHT,this.node.rotation);

    let forwardDistance = Vec3.dot(out,temVec);

    console.log(forwardDistance);

    if(forwardDistance>0 && forwardDistance<=10){

        //以玩家的Right为正前方的话,那么玩家的后方就是Forward的反方向

        let rightDistance = Vec3.dot(this.node.forward.negative(),temVec);

        if(Math.abs(rightDistance)<=3){

            console.log("在矩形里面了啊@@@@@@@@@");

        }

    }

}

我怎么看这个顶点的缠绕顺序是顺时针的?
indices[i] = vi
indices[i + 1] = vi + 3;
indices[i + 2] = vi + 1;
indices[i + 3] = vi + 2;
indices[i + 4] = vi + 3;
indices[i + 5] = vi;
结果是[0,3,1,2,3,0]。cocos的背面剔除与默认相反吗? :sweat_smile:

请问这是2D的网格还是3D的?

3D空间中绘制的网格