You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言数组一维排序与顺序存储优化及结构体替代方案问询

优化怪物跳跃方向选择的C语言实现

需求背景

原本使用二维数组float scan_fraction[4][2]存储方向分数与启用状态,int fraction_order[4]存储排序索引,现需改用结构体重构逻辑,并优化依赖随机数+goto的方向选择逻辑,消除反复尝试禁用方向的低效情况。


一、结构体重构

定义结构体封装方向的所有相关信息,替代原二维数组,提升代码可读性与可维护性:

// 方向枚举(保持原有定义)
typedef enum {
    FRAC_LEFT,
    FRAC_RIGHT,
    FRAC_BACK,
    FRAC_FORWARD,
    NUM_J_DIRS
} JumpDirection;

// 启用状态枚举
typedef enum {
    ENABLED_DISABLED,
    ENABLED_CONDITIONALLY,
    ENABLED_FULLY
} EnableState;

// 方向信息结构体:整合方向、分数、状态、向量
typedef struct {
    JumpDirection dir;
    float fraction;
    EnableState state;
    vec3_t dir_vector; // 预存方向向量,避免重复计算
} DirectionInfo;

二、核心优化:消除随机重试逻辑

核心思路是预先筛选可用方向列表,直接从列表中随机选取,彻底移除原有的goto重试逻辑:

  1. 全启用方向:遍历结构体数组,收集所有state == ENABLED_FULLY的项
  2. 排序后方向:按分数从高到低遍历,直接选取第一个可用方向
  3. 极端情况:无可用方向时,再随机选择任意方向

这种方式无需反复生成随机数重试,逻辑更线性,效率更高。


三、完整优化代码

// 排序比较函数:按分数降序排列
int compare_direction_info(const DirectionInfo* a, const DirectionInfo* b)
{
    if (a->fraction > b->fraction) return -1;
    if (a->fraction < b->fraction) return 1;
    return 0;
}

void monster_jump(edict_t *self)
{
    if(!(self->monsterinfo.aiflags & AI_JUMPDODGE) && !(self->monsterinfo.aiflags & AI_JUMPDODGEPROJ))
        return;
    if (!self->groundentity)
        return;

    vec3_t jump_dir = { 0 };

    // 初始化方向信息数组
    DirectionInfo dirs[NUM_J_DIRS] = {
        {FRAC_LEFT, 0.0f, ENABLED_DISABLED, {0}},
        {FRAC_RIGHT, 0.0f, ENABLED_DISABLED, {0}},
        {FRAC_BACK, 0.0f, ENABLED_DISABLED, {0}},
        {FRAC_FORWARD, 0.0f, ENABLED_DISABLED, {0}}
    };

    int each_tr_incomplete = 0;
    DirectionInfo* full_enabled_dirs[NUM_J_DIRS];
    int full_enabled_count = 0;

    if (self->monsterinfo.aiflags & AI_JUMPDODGEPROJ && self->monsterinfo.jump_ent)
    {
        VectorSubtract(self->s.origin, self->monsterinfo.jump_ent->s.origin, jump_dir);
        gi.bprintf(PRINT_HIGH, "monster_jump: should jump away from projectile!\n");
        jump_dir[2] *= 0.25;
        VectorNormalize(jump_dir);
        vec3_t end;
        VectorMA(self->s.origin, 128, jump_dir, end);
        trace_t tr = gi.trace(self->s.origin, NULL, NULL, end, self, MASK_SHOT);
        if (tr.fraction != 1)
        {
            VectorInverse(jump_dir);
            jump_dir[0] *= 1 + (crandom() * 0.25);
            jump_dir[1] *= 1 + (crandom() * 0.25); // 修复原代码重复乘x分量的bug
            VectorNormalize(jump_dir);
            gi.bprintf(PRINT_HIGH, "monster_jump: path blocked, jump randomly towards projectile\n");
        }
    }
    else
    {
        gi.bprintf(PRINT_HIGH, "monster_jump: should jump away from danger!\n");
        
        // 扫描各个方向并填充结构体数据
        dirs[FRAC_LEFT].fraction = scan_dir(self, SCAN_LEFT, 128, dirs[FRAC_LEFT].dir_vector);
        dirs[FRAC_RIGHT].fraction = scan_dir(self, SCAN_RIGHT, 128, dirs[FRAC_RIGHT].dir_vector);
        dirs[FRAC_BACK].fraction = scan_dir(self, SCAN_BACKWARDS, 128, dirs[FRAC_BACK].dir_vector);
        dirs[FRAC_FORWARD].fraction = scan_dir(self, SCAN_FORWARD, 128, dirs[FRAC_FORWARD].dir_vector);

        gi.bprintf(PRINT_HIGH, "monster_jump: left = %f, right = %f, back = %f, forw = %f!\n", 
                   dirs[FRAC_LEFT].fraction, dirs[FRAC_RIGHT].fraction, 
                   dirs[FRAC_BACK].fraction, dirs[FRAC_FORWARD].fraction);
        
        // 检查是否所有轨迹都不完整
        each_tr_incomplete = (dirs[FRAC_LEFT].fraction != COMPLETE_TR_FRACTION &&
                              dirs[FRAC_RIGHT].fraction != COMPLETE_TR_FRACTION &&
                              dirs[FRAC_BACK].fraction != COMPLETE_TR_FRACTION &&
                              dirs[FRAC_FORWARD].fraction != COMPLETE_TR_FRACTION);

        // 填充启用状态并收集全启用方向
        for (int i = 0; i < NUM_J_DIRS; i++)
        {
            if (dirs[i].fraction == COMPLETE_TR_FRACTION)
            {
                dirs[i].state = ENABLED_FULLY;
                full_enabled_dirs[full_enabled_count++] = &dirs[i];
            }
            else if (each_tr_incomplete)
            {
                dirs[i].state = ENABLED_CONDITIONALLY;
            }
            else if (dirs[i].fraction > 0.85f)
            {
                dirs[i].state = ENABLED_FULLY;
                full_enabled_dirs[full_enabled_count++] = &dirs[i];
            }
        }

        if (self->monsterinfo.aiflags & AI_JUMPATTACK && dirs[FRAC_FORWARD].fraction > 1.0f)
        {
            self->monsterinfo.aiflags &= ~AI_JUMPATTACK;
            gi.bprintf(PRINT_HIGH, "monster_jump: should jump forward!\n");
            VectorSubtract(self->s.origin, dirs[FRAC_FORWARD].dir_vector, jump_dir);
        }
        else
        {
            gi.bprintf(PRINT_HIGH, "monster_jump: sorting directions!\n");
            
            // 按分数降序排序方向数组
            qsort(dirs, NUM_J_DIRS, sizeof(DirectionInfo), 
                  (int (*)(const void*, const void*))compare_direction_info);

            // 选择跳跃方向
            if (full_enabled_count > 0)
            {
                // 从全启用方向中随机选一个
                int rand_idx = rand() % full_enabled_count;
                DirectionInfo* selected = full_enabled_dirs[rand_idx];
                switch(selected->dir)
                {
                    case FRAC_LEFT:
                        gi.bprintf(PRINT_HIGH, "monster_jump: JUMP LEFT(FULL + RANDOM)\n");
                        VectorSubtract(self->s.origin, dirs[FRAC_RIGHT].dir_vector, jump_dir);
                        break;
                    case FRAC_RIGHT:
                        gi.bprintf(PRINT_HIGH, "monster_jump: JUMP RIGHT(FULL + RANDOM)\n");
                        VectorSubtract(self->s.origin, dirs[FRAC_LEFT].dir_vector, jump_dir);
                        break;
                    case FRAC_BACK:
                        gi.bprintf(PRINT_HIGH, "monster_jump: JUMP BACK(FULL + RANDOM)\n");
                        VectorSubtract(self->s.origin, dirs[FRAC_FORWARD].dir_vector, jump_dir);
                        break;
                    case FRAC_FORWARD:
                        gi.bprintf(PRINT_HIGH, "monster_jump: JUMP FRONT(FULL + RANDOM)\n");
                        VectorSubtract(self->s.origin, dirs[FRAC_BACK].dir_vector, jump_dir);
                        break;
                }
            }
            else
            {
                // 从排序后的方向中找第一个可用的
                DirectionInfo* selected = NULL;
                for (int i = 0; i < NUM_J_DIRS; i++)
                {
                    if (dirs[i].state != ENABLED_DISABLED)
                    {
                        selected = &dirs[i];
                        break;
                    }
                }

                if (selected != NULL)
                {
                    switch(selected->dir)
                    {
                        case FRAC_LEFT:
                            gi.bprintf(PRINT_HIGH, "monster_jump: JUMP LEFT(sorted)\n");
                            VectorSubtract(self->s.origin, dirs[FRAC_RIGHT].dir_vector, jump_dir);
                            break;
                        case FRAC_RIGHT:
                            gi.bprintf(PRINT_HIGH, "monster_jump: JUMP RIGHT(sorted)\n");
                            VectorSubtract(self->s.origin, dirs[FRAC_LEFT].dir_vector, jump_dir);
                            break;
                        case FRAC_BACK:
                            gi.bprintf(PRINT_HIGH, "monster_jump: JUMP BACK(sorted)\n");
                            VectorSubtract(self->s.origin, dirs[FRAC_FORWARD].dir_vector, jump_dir);
                            break;
                        case FRAC_FORWARD:
                            gi.bprintf(PRINT_HIGH, "monster_jump: JUMP FRONT(sorted)\n");
                            VectorSubtract(self->s.origin, dirs[FRAC_BACK].dir_vector, jump_dir);
                            break;
                    }
                }
                else
                {
                    // 极端情况:无可用方向,随机选一个
                    int rand_dir = rand() % NUM_J_DIRS;
                    gi.bprintf(PRINT_HIGH, "monster_jump: choosing from random dir!\n");
                    switch(rand_dir)
                    {
                        case FRAC_LEFT:
                            gi.bprintf(PRINT_HIGH, "monster_jump: JUMP LEFT(RANDOM)\n");
                            VectorSubtract(self->s.origin, dirs[FRAC_RIGHT].dir_vector, jump_dir);
                            break;
                        case FRAC_RIGHT:
                            gi.bprintf(PRINT_HIGH, "monster_jump: JUMP RIGHT(RANDOM)\n");
                            VectorSubtract(self->s.origin, dirs[FRAC_LEFT].dir_vector, jump_dir);
                            break;
                        case FRAC_BACK:
                            gi.bprintf(PRINT_HIGH, "monster_jump: JUMP BACK(RANDOM)\n");
                            VectorSubtract(self->s.origin, dirs[FRAC_FORWARD].dir_vector, jump_dir);
                            break;
                        case FRAC_FORWARD:
                            gi.bprintf(PRINT_HIGH, "monster_jump: JUMP FRONT(RANDOM)\n");
                            VectorSubtract(self->s.origin, dirs[FRAC_BACK].dir_vector, jump_dir);
                            break;
                    }
                }
            }
            VectorNormalize(jump_dir);
        }
    }
    
    VectorMA(self->velocity, 300 + (100 * random()), jump_dir, self->velocity);
    self->velocity[2] += 150 + random() * 50;
    self->monsterinfo.jump_ent = NULL;
    self->monsterinfo.aiflags &= ~AI_JUMPDODGEPROJ;
    self->monsterinfo.aiflags &= ~AI_JUMPDODGE;

    gi.sound(self, CHAN_AUTO, gi.soundindex(va("player/step%i.wav", rand() % 5)), 1, ATTN_IDLE, 0);
    gi.sound(self, CHAN_AUTO, gi.soundindex(va("player/step%i.wav", rand() % 5)), 1, ATTN_IDLE, 0.05f);
}

关键优化点

  1. 结构体封装:将方向的枚举、分数、状态、向量整合到一处,消除原代码中的魔法下标操作,提升可读性。
  2. 移除重试逻辑:通过预收集可用方向列表,直接随机选取,彻底删除原有的goto跳转,代码流程更线性。
  3. 修复原代码bug:修正了原代码中jump_dir[0]重复相乘的错误,改为对x、y分量都添加随机偏移。
  4. 预存方向向量:扫描方向时直接存储向量,避免后续重复计算,提升效率。

内容的提问来源于stack exchange,提问作者Woju

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 16:35:34