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重试逻辑:
- 全启用方向:遍历结构体数组,收集所有
state == ENABLED_FULLY的项 - 排序后方向:按分数从高到低遍历,直接选取第一个可用方向
- 极端情况:无可用方向时,再随机选择任意方向
这种方式无需反复生成随机数重试,逻辑更线性,效率更高。
三、完整优化代码
// 排序比较函数:按分数降序排列 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); }
关键优化点
- 结构体封装:将方向的枚举、分数、状态、向量整合到一处,消除原代码中的魔法下标操作,提升可读性。
- 移除重试逻辑:通过预收集可用方向列表,直接随机选取,彻底删除原有的
goto跳转,代码流程更线性。 - 修复原代码bug:修正了原代码中
jump_dir[0]重复相乘的错误,改为对x、y分量都添加随机偏移。 - 预存方向向量:扫描方向时直接存储向量,避免后续重复计算,提升效率。
内容的提问来源于stack exchange,提问作者Woju
相关产品推荐
相关产品推荐

