栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 游戏开发 > 其他

关于C/C++游戏引擎如何判断碰撞(以SDL2为例)

其他 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

关于C/C++游戏引擎如何判断碰撞(以SDL2为例)

Version:alpha 2.4 Information: 代码摘自我自己的一个项目的一段代码 本文是类似日记形式,可以帮助需要的人,我在未来也会回来看(忘了时) 2022.3.18时我还只是12岁,许多公式不知道,故引用了网上许多博主的代码,原博客地址在注释内有所体现 这是建立在SDL2上的游戏引擎,这很低级,大佬勿喷
//精灵定义
class Spirit {
public:
    void SetPicture(SDL_Texture* image);
    void SetHitBox(const vector hit_box);
    void SetDirection(double direction);
    void SetSize(double sz);
    void SetBaseDirection(double _basedir);
    double GetSize();
    void Move(double speed);
    void Update();
    void SetPostion(SDL_LFPoint pos);
    void SetRotatePoint(SDL_LFPoint pos);
    vector GetHitBox();
    SDL_Texture* GetPicture();
    double GetDirection();
    SDL_LFPoint GetPos();
    bool Spirit::IsHitWith(const vector);
    SDL_LFPoint GetRotatePoint();
    SDL_LFPoint GetPostion();
protected:
    vector hitBox;
    vector rotateHitBox;
    SDL_LFPoint place, rotatePoint;
    SDL_Texture* picture;
    double dir, basedir; //0~360
    double size = 1.0;//x0 ~ inf
};
以下是一些你可能需要的明白的东西 首先,我们使一个精灵的碰撞箱是一个多边形用"vector"来存 (注:如果一个多边形是vector poly, 则poly[x]与poly[(x + 1) % poly.size()]连边) 那么你就看懂了bool Spirit::IsHitWith(const vector box);的定义,两个多边形碰撞箱是否碰撞 Spirit::rotateHitBox是经过旋转处理(SetDirection())后的碰撞箱 那么我们如下处理: 1.如果两个多边形有边重叠,则它们一定相交 2.如果一条直线向任意方向延伸出一条射线,则如果与另一多边形的交点个数为奇数则一个在另一个里边(如果刚好交点在顶点,则要去掉一次,不然就会多一次) 3.否则不相交 注:这适用于任意多边形(包括凹多边形)

这里使用了qq_41726230::is_intersect()来自https://blog.csdn.net/qq_41726230/article/details/105037871 和自己推的HitPoint2Line()
SDL_LFPoint HitPoint2Line(SDL_LFPoint a1, SDL_LFPoint b1, SDL_LFPoint a2, SDL_LFPoint b2) {
    
    double fa1 = (a1.y - b1.y) / (a1.x - b1.x);
    double fa2 = a1.y - fa1 * a1.x;
    double fb1 = (a2.y - b2.y) / (a2.x - b2.x);
    double fb2 = a2.y - fb1 * a2.x;
    
    double x = (fb2 - fa2) / (fa1 - fb1);
    double y = fa1 * x + fa2;
    return { x, y };
}

bool Spirit::IsHitWith(const vector box) {
	if (box.empty() || this->hitBox.empty()) return false;
	for (int i = 0; i < this->hitBox.size(); i++) {
		qq_41726230::line la(
			this->rotateHitBox[i].x * this->size + this->place.x, this->rotateHitBox[i].y * this->size + this->place.y,
			this->rotateHitBox[i == this->rotateHitBox.size() - 1 ? 0 : i + 1].x * this->size + this->place.x,
			this->rotateHitBox[i == this->rotateHitBox.size() - 1 ? 0 : i + 1].y * this->size + this->place.y
		);
		for (int j = 0; j < box.size(); j++) {
			qq_41726230::line lb(
				box[j].x, box[j].y,
				box[j == box.size() - 1 ? 0 : j + 1].x,
				box[j == box.size() - 1 ? 0 : j + 1].y
			);
			if (qq_41726230::is_intersect(la, lb)) {
				return true;
			}
		}
	}

	//判断我是不是在他里面
	qq_41726230::line l1(this->rotateHitBox[0].x * this->size + this->place.x, this->rotateHitBox[0].y * this->size + this->place.y, -100, -100);
	int cnt[1] = {};
	for (int j = 0; j < box.size(); j++) {
		qq_41726230::line lb(
			box[j].x, box[j].y,
			box[j == box.size() - 1 ? 0 : j + 1].x,
			box[j == box.size() - 1 ? 0 : j + 1].y
		);
		int a = 0, b = 0;
		if (qq_41726230::is_intersect(l1, lb)) cnt[0]++;
		auto pos = HitPoint2Line({ l1.xa, l1.ya }, { l1.xb, l1.yb }, { lb.xa, lb.ya }, { lb.xb, lb.yb });
		if (pos.x == lb.xa && pos.y == lb.ya) {
			cnt[0]--;
		}
	}
	if (cnt[0] & 1) {
		return true;
	}
	//判断他是不是在我里面
	cnt[0] = 0;
	qq_41726230::line l6(box[0].x, box[0].y, -100, -100);
	for (int i = 0; i < this->hitBox.size(); i++) {
		qq_41726230::line la(
			this->rotateHitBox[i].x * this->size + this->place.x, this->rotateHitBox[i].y * this->size + this->place.y,
			this->rotateHitBox[i == this->rotateHitBox.size() - 1 ? 0 : i + 1].x * this->size + this->place.x,
			this->rotateHitBox[i == this->rotateHitBox.size() - 1 ? 0 : i + 1].y * this->size + this->place.y
		);
		if (qq_41726230::is_intersect(l6, la)) cnt[0]++;
		auto pos = HitPoint2Line({ l6.xa, l6.ya }, { l6.xb, l6.yb }, { la.xa, la.ya }, { la.xb, la.yb });
		if (pos.x == la.xa && pos.y == la.ya) {
			cnt[0]--;
		}
	}
	if (cnt[0] & 1) {
		return true;
	}
	//都不是
	return false;
}
此处使用了kevin_org的文章https://blog.csdn.net/QIJINGBO123/article/details/100850088 Spirit::SetDirection(double direction);用于旋转 将hitBox转成rotateHitBox,围绕着的点在SetRotatePoint()中定义 过程: 旋转整个多边形=将多边形的每个顶点旋转 CalcRotatePoint()用于计算点的旋转
void Spirit::SetDirection(double direction) {
    this->rotateHitBox.clear();
    for (int i = 0; i < this->hitBox.size(); i++) {
        this->rotateHitBox.push_back(CalcRotatePoint(this->hitBox[i], this->rotatePoint, direction));
    }
    this->dir = direction;
}
SDL_LFPoint CalcRotatePoint(const SDL_LFPoint& pos, const SDL_LFPoint& rpos, const double& direction) {
    SDL_LFPoint res;
    //thanks  kevin_org for https://blog.csdn.net/QIJINGBO123/article/details/100850088
    res.x = (pos.x - rpos.x) * cos(direction * 3.141593 / 180.0) - (pos.y - rpos.y) * sin(direction * 3.141593 / 180.0) + rpos.x;
    res.y = (pos.x - rpos.x) * sin(direction * 3.141593 / 180.0) + (pos.y - rpos.y) * cos(direction * 3.141593 / 180.0) + rpos.y;
    return res;
}
剩下的就自己看吧 对了,这只是此项目的大概10%不到,所以它无法编译,仅供参考
//Math.h
#pragma once
#include "Structure.h"
#include 
#include 
#include 
#include 

using namespace std;

//http://blog.sina.com.cn/s/blog_5d5c80840101bnhw.html
namespace ZH_CN_ZGY {
    
    extern pair PointToSegDistEx(double x, double y, double x1, double y1, double x2, double y2);
}

extern SDL_LFPoint PointToPolygonDistPos(SDL_LFPoint p, vector polygon);

extern SDL_LFPoint HitPoint2Line(SDL_LFPoint a1, SDL_LFPoint b1, SDL_LFPoint a2, SDL_LFPoint b2);

namespace mirro {
    extern double Tri(SDL_LFPoint p1, SDL_LFPoint p2);
}

extern SDL_LFPoint CirclePointCalc(double r, double angle, SDL_LFPoint mid);

//Math.cpp
#include "Math.h"

pair ZH_CN_ZGY::PointToSegDistEx(double x, double y, double x1, double y1, double x2, double y2) {
    double cross = (x2 - x1) * (x - x1) + (y2 - y1) * (y - y1);
    if (cross <= 0) return { sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1)), {x1, y1} };
    double d2 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
    if (cross >= d2) return { sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2)), {x2, y2} };
    double r = cross / d2;
    double px = x1 + (x2 - x1) * r;
    double py = y1 + (y2 - y1) * r;
    //bug fixed
    //return sqrt((x - px) * (x - px) + (py - y1) * (py - y1));
    return { sqrt((x - px) * (x - px) + (py - y) * (py - y)), {px, py} };
}

SDL_LFPoint PointToPolygonDistPos(SDL_LFPoint p, vector polygon) {
    double result_dis = 1e10;
    SDL_LFPoint result;
    for (int i = 0; i < polygon.size(); i++) {
        SDL_LFPoint a = polygon[i];
        SDL_LFPoint b = polygon[(i + 1) % polygon.size()];
        auto ret = ZH_CN_ZGY::PointToSegDistEx(p.x, p.y, a.x, a.y, b.x, b.y);
        if (ret.first < result_dis) {
            result_dis = ret.first;
            result = ret.second;
        }
    }
    return result;
}

SDL_LFPoint HitPoint2Line(SDL_LFPoint a1, SDL_LFPoint b1, SDL_LFPoint a2, SDL_LFPoint b2) {
    
    double fa1 = (a1.y - b1.y) / (a1.x - b1.x);
    double fa2 = a1.y - fa1 * a1.x;
    double fb1 = (a2.y - b2.y) / (a2.x - b2.x);
    double fb2 = a2.y - fb1 * a2.x;
    
    double x = (fb2 - fa2) / (fa1 - fb1);
    double y = fa1 * x + fa2;
    return { x, y };
}

//https://bbs.csdn.net/topics/391814854
namespace mirro {
    double Tri(SDL_LFPoint p1, SDL_LFPoint p2) {//形参是Point类的对象p1,p2
        double angle;
        if (p1.x == p2.x && p1.y < p2.y)
            return 90.0;//如果x坐标相等,p1的y值小于p2的y值,则方位角是90
        if (p1.x == p2.x && p1.y > p2.y)
            return 270.0;//如果x坐标相等,p1的y值大于p2的y值,则方位角是270
        if (p1.y == p2.y && p1.x < p2.x)
            return 0.0;//如果y坐标相等,p1的x值小于p2的x值,则方位角是180
        if (p1.y == p2.y && p1.x > p2.x)
            return 180.0;//如果y坐标相等,p1的x值大于p2的x值,则方位角是180
        angle = atan((p1.y - p2.y) / (p1.x - p2.x)) * (180.0 / M_PI); //得到弧度制的角
        if (p1.x < p2.x && p1.y < p2.y)
            return angle; //标准情况下正常输出。
        if (p1.x > p2.x)
            return 180.0 + angle;//angle正负都有可能。
        if (p1.x < p2.x && p1.y > p2.y)
            return 360.0 + angle;//angle是负值。
        return -999;
    }
}

SDL_LFPoint CirclePointCalc(double r, double angle, SDL_LFPoint mid) {
    return {
        mid.x + r * cos((angle - 90) * M_PI / 180.0),
        mid.y + r * sin((angle - 90) * M_PI / 180.0)
    };
}

//Structure.h
#pragma once

struct SDL_LFPoint {
	double x, y;
};
struct SDL_LFRect {
	double x, y, w, h;
};

//Spirit.h
#pragma once
#include "Structure.h"
#include "Math.h"
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

#define mkrect(rect, x_, y_, w_, h_) {rect.x = x_; rect.y = y_; rect.w = w_; rect.h = h_;}

//main.cpp

//const since indev1.15
extern const double SET_FPS;
extern SDL_Window* screen;
extern SDL_Renderer* renderer;
extern bool quit;
extern SDL_Event evt;

extern map TextureDB;

extern bool FontSize[256];
extern map > Font;

extern map BackGroundMusic;
extern map Sound;

extern map OpKey;

extern Uint64 nFrequency, nPrevCounter, nCurrCounter, nElapsedCounter;
extern double elapsed, totalTime, fps;
extern int fpsCount;

extern SDL_LFPoint HeartPoint;
extern double CharaHP, CharaFakeHP, GermanyHP;
extern double CharaSpeed;
extern bool SeaTeaUsed;

extern int w, h;

class Spirit {
public:
	void SetPicture(SDL_Texture* image);
	void SetHitBox(const vector hit_box);
	void SetDirection(double direction);
	void SetSize(double sz);
	void SetBaseDirection(double _basedir);
	double GetSize();
	void Move(double speed);
	void Update();
	void SetPostion(SDL_LFPoint pos);
	void SetRotatePoint(SDL_LFPoint pos);
	vector GetHitBox();
	SDL_Texture* GetPicture();
	double GetDirection();
	SDL_LFPoint GetPos();
	bool Spirit::IsHitWith(const vector);
	SDL_LFPoint GetRotatePoint();
	SDL_LFPoint GetPostion();
protected:
	vector hitBox;
	vector rotateHitBox;
	SDL_LFPoint place, rotatePoint;
	SDL_Texture* picture;
	double dir, basedir; //0~360
	double size = 1.0;//x0 ~ inf
};

bool IsHit(Spirit spi);

//Spirit.cpp
#include "Spirit.h"

SDL_LFPoint CalcRotatePoint(const SDL_LFPoint& pos, const SDL_LFPoint& rpos, const double& direction) {
	SDL_LFPoint res;
	//thanks  kevin_org for https://blog.csdn.net/QIJINGBO123/article/details/100850088
	res.x = (pos.x - rpos.x) * cos(direction * 3.141593 / 180.0) - (pos.y - rpos.y) * sin(direction * 3.141593 / 180.0) + rpos.x;
	res.y = (pos.x - rpos.x) * sin(direction * 3.141593 / 180.0) + (pos.y - rpos.y) * cos(direction * 3.141593 / 180.0) + rpos.y;
	return res;
}

void Spirit::SetPicture(SDL_Texture* image) {
	this->picture = image;
}

void Spirit::SetHitBox(const vector hit_box) {
	this->hitBox = hit_box;
	this->SetDirection(this->dir);
}

void Spirit::SetDirection(double direction) {
	this->rotateHitBox.clear();
	for (int i = 0; i < this->hitBox.size(); i++) {
		this->rotateHitBox.push_back(CalcRotatePoint(this->hitBox[i], this->rotatePoint, direction));
		//SDL_Log("indev1.13 test: new direction:%lf rotate hit box point %d:{%lf, %lf}", direction, i, this->rotateHitBox[i].x, this->rotateHitBox[i].y);
	}
	//indev1.13 testing
	
	
	this->dir = direction;
}

void Spirit::SetSize(double sz) {
	this->size = sz;
	this->SetDirection(this->dir);
}


void Spirit::SetBaseDirection(double _basedir) {
	this->basedir = _basedir;
}

double Spirit::GetSize() {
	return this->size;
}

void Spirit::Move(double speed) {
	this->place = CirclePointCalc(speed, this->dir + this->basedir, this->place);
	this->SetDirection(this->dir);
}

void Spirit::Update() {
	SDL_FRect spiritRect;
	SDL_QueryTexture(this->picture, NULL, NULL, &w, &h);
	mkrect(spiritRect, this->place.x, this->place.y, w * this->size, h * this->size);
	SDL_FPoint rotatePointX = { this->rotatePoint.x * this->size, this->rotatePoint.y * this->size };
	SDL_RenderCopyExF(renderer, this->picture, NULL, &spiritRect, this->dir, &rotatePointX, SDL_FLIP_NONE);
}

void Spirit::SetPostion(SDL_LFPoint pos) {
	this->place = pos;
	this->SetDirection(this->dir);
}

void Spirit::SetRotatePoint(SDL_LFPoint pos) {
	this->rotatePoint = pos;
	this->SetDirection(this->dir);
}

vector Spirit::GetHitBox() {
	vector result = this->rotateHitBox;
	for (auto& p : result) {
		p.x = p.x * this->size + this->place.x;
		p.y = p.y * this->size + this->place.y;
	}
	return result;
}

SDL_Texture* Spirit::GetPicture() {
	return this->picture;
}

double Spirit::GetDirection() {
	return this->dir;
}

SDL_LFPoint Spirit::GetPos() {
	return this->place;
}


namespace qq_41726230 {
	class line {
	public:
		double xa;
		double ya;
		double xb;
		double yb;
		line() {}
		line(double xa, double ya, double xb, double yb) {
			this->xa = xa;
			this->ya = ya;
			this->xb = xb;
			this->yb = yb;
		}
		double get_max_x() {
			return xa > xb ? xa : xb;
		}
		double get_min_x() {
			return xa > xb ? xb : xa;
		}
		double get_max_y() {
			return ya > yb ? ya : yb;
		}
		double get_min_y() {
			return ya > yb ? yb : ya;
		}
	};

	bool is_intersect(line myline1, line myline2) {
		if (myline1.get_max_x() < myline2.get_min_x() ||
			myline2.get_max_x() < myline1.get_min_x() ||
			myline1.get_max_y() < myline2.get_min_y() ||
			myline2.get_max_y() < myline1.get_min_y())   return false;
		double res1 = (myline1.xa - myline1.xb) * (myline2.ya - myline1.yb) - (myline1.ya - myline1.yb) * (myline2.xa - myline1.xb);
		double res2 = (myline1.xa - myline1.xb) * (myline2.yb - myline1.yb) - (myline1.ya - myline1.yb) * (myline2.xb - myline1.xb);

		double res3 = (myline2.xa - myline2.xb) * (myline1.ya - myline2.yb) - (myline2.ya - myline2.yb) * (myline1.xa - myline2.xb);
		double res4 = (myline2.xa - myline2.xb) * (myline1.yb - myline2.yb) - (myline2.ya - myline2.yb) * (myline1.xb - myline2.xb);
		if (res1 * res2 <= 0 && res3 * res4 <= 0) return true;
		else return false;
	}
}

bool Spirit::IsHitWith(const vector box) {
	if (box.empty() || this->hitBox.empty()) return false;
	for (int i = 0; i < this->hitBox.size(); i++) {
		qq_41726230::line la(
			this->rotateHitBox[i].x * this->size + this->place.x, this->rotateHitBox[i].y * this->size + this->place.y,
			this->rotateHitBox[i == this->rotateHitBox.size() - 1 ? 0 : i + 1].x * this->size + this->place.x,
			this->rotateHitBox[i == this->rotateHitBox.size() - 1 ? 0 : i + 1].y * this->size + this->place.y
		);
		for (int j = 0; j < box.size(); j++) {
			qq_41726230::line lb(
				box[j].x, box[j].y,
				box[j == box.size() - 1 ? 0 : j + 1].x,
				box[j == box.size() - 1 ? 0 : j + 1].y
			);
			if (qq_41726230::is_intersect(la, lb)) {
				//SDL_Log("Line %d{a:{%lf, %lf} b:{%lf, %lf}} with %d{a:{%lf, %lf} b:{%lf, %lf}}", i, this->rotateHitBox[i].x + this->place.x, this->rotateHitBox[i].y + this->place.y, this->rotateHitBox[i == this->hitBox.size() - 1 ? 0 : i + 1].x + this->place.x, this->rotateHitBox[i == this->hitBox.size() - 1 ? 0 : i + 1].y + this->place.y, j, box[j].x, box[j].y, box[j == box.size() - 1 ? 0 : j + 1].x, box[j == box.size() - 1 ? 0 : j + 1].y);
				return true;
			}
		}
	}

	/
	int cnt[1] = {};
	for (int j = 0; j < box.size(); j++) {
		qq_41726230::line lb(
			box[j].x, box[j].y,
			box[j == box.size() - 1 ? 0 : j + 1].x,
			box[j == box.size() - 1 ? 0 : j + 1].y
		);
		int a = 0, b = 0;
		if (qq_41726230::is_intersect(l1, lb)) cnt[0]++;
		auto pos = HitPoint2Line({ l1.xa, l1.ya }, { l1.xb, l1.yb }, { lb.xa, lb.ya }, { lb.xb, lb.yb });
		if (pos.x == lb.xa && pos.y == lb.ya) {
			cnt[0]--;
		}
		
	}
	
	if (cnt[0] & 1) {
		return true;
	}
	//判断他是不是在我里面
	//memset(cnt, 0, sizeof cnt);
	cnt[0] = 0;
	qq_41726230::line l6(box[0].x, box[0].y, -100, -100);
	
	for (int i = 0; i < this->hitBox.size(); i++) {
		qq_41726230::line la(
			this->rotateHitBox[i].x * this->size + this->place.x, this->rotateHitBox[i].y * this->size + this->place.y,
			this->rotateHitBox[i == this->rotateHitBox.size() - 1 ? 0 : i + 1].x * this->size + this->place.x,
			this->rotateHitBox[i == this->rotateHitBox.size() - 1 ? 0 : i + 1].y * this->size + this->place.y
		);
		if (qq_41726230::is_intersect(l6, la)) cnt[0]++;
		auto pos = HitPoint2Line({ l6.xa, l6.ya }, { l6.xb, l6.yb }, { la.xa, la.ya }, { la.xb, la.yb });
		if (pos.x == la.xa && pos.y == la.ya) {
			cnt[0]--;
		}
		
	}
	
	if (cnt[0] & 1) {
		return true;
	}
	//都不是
	return false;
}

SDL_LFPoint Spirit::GetRotatePoint() {
	return this->rotatePoint;
}

//I found that I had something wrong in my head because there is the Spirit::GetPos().
SDL_LFPoint Spirit::GetPostion() {
	return this->place;
}

bool IsHit(Spirit spi) {
	SDL_QueryTexture(TextureDB["RedHeart"], NULL, NULL, &w, &h);
	if (spi.IsHitWith(
		{
			{HeartPoint.x, HeartPoint.y},
			{HeartPoint.x + w, HeartPoint.y},
			{HeartPoint.x + w, HeartPoint.y + h},
			{HeartPoint.x, HeartPoint.y + h}
		}
	)) {
		return true;
	}
	return false;
}
在CSDN上发布,原文链接:https://dscs2009.blog.luogu.org/guan-yu-cc-you-hu-yin-qing-ru-he-pan-duan-peng-zhuang-yi-sdl2-wei-li-post 这本来就是我写的,自己转载。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/901824.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号