c# 手把手教你撸一个吃鸡游戏跑圈机制

 最近迷上吃鸡游戏,慢慢对他的跑圈机制产生了兴趣,于是就试着写了个吃鸡游戏跑圈机制出来~~~

一、话不多少,先上跑圈效果图:

二、知识提要:

 1.C# winform程序中比较简单的绘图控件就是 PictureBox

 用到的主要辅助类有:Bitmap,Graphics,Brush,Pen。用形象的方式介绍下他们:PictureBox控件相当于一块土地,为画图腾出空间;但有了土地不能直接开始画,因为没有画板对吧,所以Bitmap相当于画布或者画纸,用于覆盖在地面上,方便我们进行画图;Graphics就是画图的手;Pen就是画笔很简单;但是有了画笔,不知道笔的粗细和颜色,那么Brush就是用来定义这两者的。

int width = pBox_home.Width; //获取PictureBox的宽高
int height = pBox_home.Height;

map_bg = new Bitmap(width, height); //设置要涂改的背景
mGraphics = Graphics.FromImage(map_bg); //设置设置画笔

pBox_home.Image = map_bg;   //添加背景  

mBrush = new SolidBrush(Color.Blue);
mPen_outer = new Pen(mBrush, 2);

mBrush = new SolidBrush(Color.White);
mPen_inner = new Pen(mBrush, 2);

 2.在C#语言中,Y轴是朝下的

 3.Graphics中的 DrawEllipse(Pen pen, int x, int y, int width, int height) 是我们主要用到的画圆函数

 他将椭圆和标准圆整合到一起。说下每个参数的意思:

 pen:用来画椭圆(圆)的笔

 x:画圆的x坐标

 y:画圆的y坐标

 width:长轴

 height:短轴(不清楚椭圆的长轴,短轴可以自行百度下~)

 在这个函数中有2个坑,看清楚了别陷阱去了!

 ①这里的 x 和 y 指的是将整个圆(椭圆)包含在其中的最小的正方形(矩形)的最左上角那个点。意思就是如果我们想在(100,100)这个点画一个半径为20的圆的话,必须要减去圆的半径,在(90,90)上调用这个函数才能达到效果,如下图:

 ②然后是 width 和 height,指的是长轴和短轴,换做是圆则长轴和短轴相等,但这个长度量实际上指的是直径,不是半径啊!记清楚了哟!

public void drawCircle(Pen pen, PointF point, float radius) //自定义画圆函数
{
	//将想要画的位置减去半径radius, 以及radius*2 代表的就是①和②的意思
	Rectangle mRect = new Rectangle((int)(point.X - radius), (int)(point.Y - radius), (int)(radius * 2), (int)(radius * 2));

	mGraphics.DrawEllipse(pen, mRect);
}

三、具体讲解:

 难点1:跑圈时内部安全区动态创建问题

 玩过吃鸡的朋友都知道,在一定时间到后开始跑圈,内外两个圆重合时,随机加载下一个安全区,但这个安全区不能超出外圆原来的界限,必须把整个新的安全区包含在内,那么如何来实现这一点,如下图:

 灰色圆圈的半径为 R1-R2,实际上内部安全区的新圆心只能在灰色圆圈上及以内产生,如果在图上绿色随机产生点的画,那么画出来的新安全区就超出外圈的界限了。

/// <summary>
/// 在圆心为point,半径为radius的圆内,产生一个半径为radius_inner的圆的圆心
/// </summary>
/// <param name="point">外圆圆心</param>
/// <param name="radius_outer">外圆半径</param>
/// <param name="radius_inner">内圆半径</param>
/// <returns>内圆圆心</returns>
public PointF PointOfRandom(PointF point, float radius_outer, float radius_inner)
{
	int x = random.Next((int)(point.X - (radius_outer - radius_inner)), (int)(point.X + (radius_outer - radius_inner)));
	int y = random.Next((int)(point.Y - (radius_outer - radius_inner)), (int)(point.Y + (radius_outer - radius_inner)));
			
	while (!isInRegion(x - point.X, y - point.Y, radius_outer - radius_inner))
	{
		x = random.Next((int)(point.X - (radius_outer - radius_inner)), (int)(point.X + (radius_outer - radius_inner)));
		y = random.Next((int)(point.Y - (radius_outer - radius_inner)), (int)(point.Y + (radius_outer - radius_inner)));
	}

	PointF p = new PointF(x, y);
	return p;
}
		
/// <summary>
/// 
/// </summary>
/// <param name="x_off">与大圆圆心的x方向偏移量</param>
/// <param name="y_off">与大圆圆心的y方向偏移量</param>
/// <param name="distance">大圆与小圆半径的差</param>
/// <returns>判断点是否在范围内</returns>
public bool isInRegion(float x_off, float y_off, float distance)
{
	if (x_off * x_off + y_off * y_off <= distance * distance)
	{
		return true;
	}
	return false;
}
		

 难点2:毒圈和安全域相内切问题

 在缩圈时,外部毒圈不断缩小,有那么个时刻,内圈和外圈相内切即有且仅有一个点重合,这是的状态为内切,如下图:

 那么这个重合点必定在外圈和内圈圆心所在的直线上,相信这个不难理解。那么此刻的判定条件就是 相差距离 = 0,即R2+两圆心距离 = R1。

/// <summary>
/// 判断两个圆是否重合,或者是相内切
/// </summary>
/// <param name="p_outer">外圆圆心</param>
/// <param name="r_outer">外圆半径</param>
/// <param name="p_inner">内圆圆心</param>
/// <param name="r_inner">内圆半径</param>
/// <returns>是否相内切</returns>
public bool isIntersect(PointF p_outer, float r_outer, PointF p_inner, float r_inner)
{
	//判定条件:两圆心的距离 + r_inner = r_outer

float distance = (float)Math.Sqrt((p_outer.X - p_inner.X) * (p_outer.X - p_inner.X) + (p_outer.Y - p_inner.Y) * (p_outer.Y - p_inner.Y));

	if (distance + r_inner >= r_outer)
	{
		return true;
	}
	return false;
}

 难点3:内切后的运行状态?

 实际上在内切后,外圈的圆心以直线的方式不断向内圈圆心靠近,这条直线就是外圈圆心和内圈圆心所在的直线,并且外圈半径依旧在不断变小,如下图:

 直线的斜率和直线方程在图上,我就不做过多解释,相信大家能看懂。值得一提的是,在人为设定外圈半径每次减少1个单位长度的情况下,即外圈圆心在所在直线上向内圈圆心移动1个单位,所以 x_off^2 + y_off^2 = 1,那么在知道斜率k和直角三角形中斜边为1的情况下就能够求出每次外圈圆心 x 和 y的移动距离。那么这个过程的结束条件就是:外圈半径r1 = 内圈半径r2。

if (mRadius_outer != mRadius_inner)  //外圈和内圈圆心重合,半径相同
{
	// k = y/x
	// y = kx
	// x^2+y^2 = 1
	// x^2 = 1/(k^2+1)
	float k = (mPoint_outer.Y - mPoint_inner.Y) / (mPoint_outer.X - mPoint_inner.X);

	float x_off = 1 * (float)Math.Sqrt(1 / (k * k + 1));

	// 通过mPoint_outer和mPoint_inner的x坐标来判断此时外圆圆心要移动的是该 + x_off(x轴偏移量)还是 -x_off
	mPoint_outer.X += 1 * (mPoint_outer.X < mPoint_inner.X ? 1 : -1) * x_off;
	// 知道变化后的外圈圆心的x坐标,和直线方程来求对应的y坐标
	mPoint_outer.Y = k * (mPoint_outer.X - mPoint_inner.X) + mPoint_inner.Y;

	mDrawHelper.drawCircle(mPen_outer, mPoint_outer, mRadius_outer);
	mDrawHelper.drawCircle(mPen_inner, mPoint_inner, mRadius_inner);
}
		

 至此,所有的问题都解决了,快去撸一个你自己的吃鸡跑圈游戏吧~(滑稽脸)

四、源代码获取:

github地址:https://github.com/lawler61/chicken

发布了18 篇原创文章 · 获赞 21 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_35561857/article/details/79182790