记2D游戏角色重叠的优化

本文讲述了作者在扩展2D游戏中小队角色数量后遇到角色堆叠问题,通过添加权重机制和优化移动逻辑,确保角色在队长周围分散,同时考虑保持队形和避开微小移动。
摘要由CSDN通过智能技术生成

笔者现在参与开发的 2d游戏 最近扩展了小队的角色数量 由之前的5人 扩展到了8人,经常出现多个小人叠在一起的情况。现在着手解决这个问题。

最早的逻辑是 小队中有一名队长 玩家可以操作控制队长移动,所有成员在距队长超过指定距离时会触发跟随逻辑,移动到队长所在位置

leader: moveTo(x,y);
other1: moveTo(leader.x,leader.y);
 ... 
othern: moveTo(leader.x,leader.y);

在这里插入图片描述
在到达指定点后小人叠在一起在一起,想要避免这种情况,加入碰撞检测无疑是最好的,但是对于初始设计就没有碰撞逻辑的游戏来说改造成本过高。待机时的小队成员堆叠最难看,所以先从这个这块开始优化。

早期的优化方案是每个人随机落后主角一点距离:

leader: moveTo(x,y);

//三目运算符处的逻辑是让队员不会超过队长
other1: dx1=rand(50,100);dx2=rand(50,100);moveTo(leader.x + (leader.x>other1.x?dx1:-dx1),leader.y+(leader.y>other1.y?dy1:-dy1));
 ... 
othern:dxn=rand(50,100);dxn=rand(50,100);moveTo(leader.x + (leader.x>othern.x?dxn:-dxn),leader.y+(leader.y>othern.y?dyn:-dyn));

在这里插入图片描述
这个方案在(小队人数<5)效果还是可以的,但是队伍扩充到8人时,堆叠的现象还是比较容易出现
在这里插入图片描述
笔者的想法是 在队长周围预设一些位置,在移动结束后 所有跟随队长的队员向队长申请一个权重最小位置,然后移动到指定位置,并增加这个位置的权重
在这里插入图片描述

class leader {
	int around_sites[8]; //记录周围8个位置的权重 
	
	int getASite() {
		//遍历around_sites 找到权重最低的位置
		vector<int> vec_idx;
		for(){
		...
		}
		
		//vec_idx 中随机一个位置
		idx = vec_idx[wrand(vec_idx.size())];
		
		//增加权重
		around_sites[idx] += 1;
		
		if (idx>= 4) //自己在4号位 这里+1 后边计算位置好算一些 
			return idx+1
		return idx
	} 
	
	void clearSites() {
		//清空权重
		memset(around_sites,0,sizeof(around_sites));
	}

	void onMoveEnd() {
		//在队长移动结束后清空权重
		clearSites();
	}
	
	...
	
}

class other {
	int site = -1; //位于队长周围的位置编号

	onIdle() {
		if (离队长太远了)
			moveToLeader();
			return;

		if (队长在移动) {
			return;
		}
		
		//在队长周围,且队长没有移动,在队长附近找一个较优位置并前往
		if (site<0) {
			site = leader->getASite();
			moveToLeader(site);
		}
	}

	//这个要结合游戏在适当的位置做重置
	void resetSite() {
		site = -1;
	}
	
	void moveToLeader(int site=-1) {
		int x,y;
		if (site<0) {
			//这里可以结合上边的优化处理
			x = leader.x + dx;
			y = leader.y + dy;	
		} else {
			// 6 | 3 | 0
	        // 7 | 4 | 1
	        // 8 | 5 | 2
	        x = leader.x + (site / 3 - 1) * GRIDSIZE; //GRIDSIZE:地图网格的尺寸
	        y = leader.y + (site % 3 - 1) * GRIDSIZE;
		}
		moveTo(x, y)
	}
}

目前看效果还可以,先这样,后边有优化再做补充。

策划提出两个优化 :

1.每次移动后尽量保持原有阵型
2.当队长进行微小移动到队员位置时队员要避开

解决方案:队员在调整位置之前先通过坐标差值计算出自己相对于队长的位置, 配合权重按照一定的优先级来选择目标位置。

//site优先级
static const short SitePriority[9][8] = {
    {0, 1, 3, 2, 6, 5, 7, 8}, // 0
    {1, 0, 2, 3, 5, 7, 6, 8}, // 1
    {2, 1, 5, 0, 8, 3, 7, 6}, // 2
    {3, 0, 6, 1, 7, 5, 2, 8}, // 3
    {1, 3, 5, 7, 0, 2, 6, 8}, // 4
    {5, 2, 8, 1, 7, 3, 0, 6}, // 5
    {6, 7, 3, 0, 8, 5, 1, 2}, // 6
    {7, 6, 8, 3, 5, 1, 2, 0}, // 7
    {8, 7, 5, 6, 2, 1, 3, 0}  // 8
};

class leader {
	map<int,short> around_site_weight; // 周围点位的占用权重
	
	leader():around_site_weight({{0, 0}, {1, 0}, {2, 0}, {3, 0}, {5, 0}, {6, 0}, {7, 0}, {8, 0}}) {
		//构造函数
	}
	
	int getASite(int prefer_site) {
		int site = 0;
	    short weight = 999;
	    auto sites = SitePriority[prefer_site];
	    for (int i = 0; i < 8; i++) {
	        // 权重相同的按照优先级来排,权重不同的按权重来取
	        auto s = sites[i];
	        auto w = around_site_weight[s];
	        if (w >= 0 && weight > w) {
	            site = s;
	            weight = w;
	        }
        }
			
		//增加权重
		around_sites[site] += 1;
		return site;
	} 
	
	void clearSites() {
		//清空权重 这里可以再判断一下周围的位置 是阻挡则将权重设为-1
		memset(around_sites,0,sizeof(around_sites));
		
		...
	}

	void onMoveEnd() {
		//在队长移动结束后清空权重
		clearSites();
	}
	
	...
	
}

class other {
	int site = -1; //位于队长周围的位置编号

	onIdle() {
		if (离队长太远了)
			moveToLeader();
			return;

		if (队长在移动) {
			resetSite(); //解决问题2
			return;
		}
		
		//在队长周围,且队长没有移动,在队长附近找一个较优位置并前往
		if (site<0) {
			int prefer_site = calcNowSite(); //解决问题1
			site = leader->getASite(prefer_site);
			moveToLeader(site);
		}
	}

	//这个要结合游戏在适当的位置做重置
	void resetSite() {
		site = -1;
	}

	//这个函数用来计算自身相对于队长的位置,用来解决尽量保持队形的问题
	int calcNowSite()
	{
	    const Point &mpos = leader->GetPosition();
	    const Point &spos = this->GetPosition();
	
	    int site = 4; //队长所在的位置
	    int dx = spos.x - mpos.x;
	    int dy = spos.y - mpos.y;
	    if (abs(dx) < GRIDSIZE / 2 && abs(dy) < GRIDSIZE / 2) {
	        return site; //队长太近了
	    }
	
		//这里按斜率坐划分
	    if (dx == 0) {
	        site = dy > 0 ? 5 : 3;
	    } else {
	        float y_x = (float)dy / (float)dx;
	        if (y_x > 2.0f || y_x < -2.0f) {
	            site = dy > 0 ? 5 : 3;
	        } else if (y_x > 0.5f) {
	            site = dx > 0 ? 2 : 6;
	        } else if (y_x > -0.5f) {
	            site = dx > 0 ? 1 : 7;
	        } else {
	            site = dx > 0 ? 0 : 8;
	        }
	    }

	    return site;
	}
	
	void moveToLeader(int site=-1) {
		int x,y;
		if (site<0) {
			//这里可以结合上边的优化处理
			x = leader.x + dx;
			y = leader.y + dy;	
		} else {
			// 6 | 3 | 0
	        // 7 | 4 | 1
	        // 8 | 5 | 2
	        x = leader.x + (site / 3 - 1) * GRIDSIZE; //GRIDSIZE:地图网格的尺寸
	        y = leader.y + (site % 3 - 1) * GRIDSIZE;
		}
		moveTo(x, y)
	}
}

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值