pygame飞机大战用精灵组层编写敌机系列(一)敌机的运动轨迹,贝塞尔曲线方程及杨辉三角计算

飞机大战中,敌机的角色是必不可少的,其运动轨迹有很多种,简单点的,用初中、高中的几何知识,也能创建不少。

例如:圆圈

radius = 200
for angle in range(0,361,10):
    a = angle*math.pi/180
    pos_x = radius *math.sin(a)
    pos_y = radius *math.cos(a)

稍微复杂点 sin函数,控制下中心点y坐标(300)和振幅(200),也能实现很多

for x in range(0,SCENEWIDTH):
    pos_x = x
    angle = x *math.pi/180
    pos_y = 200*math.sin(angle)+300

再例如抛物线函数,螺旋曲线函数,都能实现飞机的各种花样轨迹。

调试下各个参数的数值,顺便温故下初中高中的知识,也是很有乐趣的。

但简单、易行且路径花样繁多的公式,还是得用到贝塞尔曲线方式。可以自行百度,太神奇了。

参考下阿呆的入门介绍。

http://www.cnblogs.com/wjtaigwh/p/6647114.html

看下n 阶贝塞尔曲线计算公式实现

https://www.jianshu.com/p/7c56103dcf63

编写了一个容易理解点的 python的实现方式。

用到了下面的知识点

----------------------------------------------------------------

  • N = 3: P = (1-t)^2P0 + 2(1-t)tP1 + t^2*P2
  • N = 4: P = (1-t)^3P0 + 3(1-t)^2tP1 + 3(1-t)t^2P2 + t^3*P3
  • N = 5: P = (1-t)^4P0 + 4(1-t)^3tP1 + 6(1-t)2*t2P2 + 4(1-t)t^3P3 + t^4*P4

将贝塞尔曲线一般参数公式中的表达式用如下方式表示:
设有常数 a,b 和 c,则该表达式可统一表示为如下形式:
a * (1 - t)^b * t^c * Pn;

根据上面的分析就可以总结出 a,b,c 对应的取值规则:

  • b: (N - 1) 递减到 0 (b 为 1-t 的幂)
  • c: 0 递增到 (N - 1) (c 为 t 的幂)
  • a: 在 N 分别为 1,2,3,4,5 时将其值用如下形式表示: 
  • N=1:---------1
    N=2:--------1 1
    N=3:------1 2 1
    N=4:-----1 3 3 1
    N=5:---1 4 6 4 1
    a 值的改变规则为: 杨辉三角

-------------------------------------------------------------------

理论基础有了,开始写代码

a 值用杨辉三角计算,b ,c 值在for 循环里计算,Pn从传入的点坐标读取。

二阶,三阶直接用展开后的公式简单。

对于n阶要用杨辉三角。杨辉三角网上的计算代码很简洁,高效,可阅读起来不直观,我简单的把C++的实现方式改了下,看起来容易理解多了。

"""
杨辉三角的数的规律
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
……
"""

def YangHui(n):
    #初设化,并赋值为0,建立二维数组,res[i][i],i=0~n-1;
    # 其实也可以直接建立 res[n][n]数组。
    res = [[0 for i in range(j + 1)] for j in range(n)]
    #左右两边赋值1
    for i in range(0, n):
        res[i][0] = 1
        res[i][i] = 1
    #从第二个数开始,等于前列的左边一个加上正上方一个
    for row in range(1, n):
        for col in range(1, row):
            res[row][col] = res[row - 1][col - 1] + res[row - 1][col]
    return res

返回的是n阶要用到的a值的一个二维数组。

下面开始进行贝塞尔的计算了。points为控制点的列表,格式为points = [(10, 40), (200, 400), (450, 200), (100, 30)]之类,包含两个坐标,t的值在[0,1]之间,含义和公式里的t一致。p[v][0] 公式里的第n个点(Pn)的x值

def bezier(points, t):
    n = len(points)
    pointx = 0
    pointy = 0
    nrow_a = YangHui(n)
    for v in range(0, n):
        c = v
        b = n - 1 - c
        a = nrow_a[n - 1][v]
        # print('a:', a, "b:", b, "c:", c)
        # print(points[v][0], points[v][1])

        #a * (1 - t)^b * t^c * Pn
        pointx += a * math.pow((1 - t), b) * math.pow(t, c) * points[v][0]
        pointy += a * math.pow((1 - t), b) * math.pow(t, c) * points[v][1]
    return pointx, pointy

返回的是坐标点的x,y值。

开始测试代码,其实pygame提供了贝塞尔曲线的绘制函数,源代码里也有(可惜我看不懂)。

用我们自己写的代码来绘制,比较下pygame提供的函数绘制,看看效果如何。为了体现贝塞尔方程是优势,也顺便画了直线方程。

def main():

    surf = pygame.display.set_mode((600,800), 1, 32)
    pygame.display.set_caption("贝塞尔曲线测试")
    surf.fill((0, 0, 0))
    #四个测试点
    points = [(10, 40), (200, 400), (450, 200), (100, 30)]
    #pygame提供的函数
    col = (255, 0, 0)
    pygame.gfxdraw.bezier(surf, points,10, col)
    #画直线 
    col2 = (0, 255, 0)
    pygame.draw.line(surf, col2, (10, 40), (200, 400), 10)
    pygame.draw.line(surf, col2, ((200, 400)), (450, 200), 10)
    pygame.draw.line(surf, col2, ((450, 200)), (100, 30), 10)
    #自己编写的函数实现
    col3 = (0, 0, 255)
    point2 = []
    for v in range(0, 101):
        #0.01递增,共100个点
        t = v / 100
        point2.append(bezier(points, t))
    for v in range(1, 101):
        pygame.draw.line(surf, col3, point2[v - 1], point2[v], 1)
    pygame.display.flip()
    
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                exit()

main()

可以看到红蓝线是重合的,肉眼很难分辨坐标点差别。

pygame的实现方式在SDL_gfxPrimitives.c里,源代码如下:
 

/* ---- Bezier curve */

/*!
\brief Internal function to calculate bezier interpolator of data array with ndata values at position 't'.

\param data Array of values.
\param ndata Size of array.
\param t Position for which to calculate interpolated value. t should be between [0, ndata].

\returns Interpolated value at position t, value[0] when t<0, value[n-1] when t>n.
*/
double _evaluateBezier (double *data, int ndata, double t) 
{
	double mu, result;
	int n,k,kn,nn,nkn;
	double blend,muk,munk;

	/* Sanity check bounds */
	if (t<0.0) {
		return(data[0]);
	}
	if (t>=(double)ndata) {
		return(data[ndata-1]);
	}

	/* Adjust t to the range 0.0 to 1.0 */ 
	mu=t/(double)ndata;

	/* Calculate interpolate */
	n=ndata-1;
	result=0.0;
	muk = 1;
	munk = pow(1-mu,(double)n);
	for (k=0;k<=n;k++) {
		nn = n;
		kn = k;
		nkn = n - k;
		blend = muk * munk;
		muk *= mu;
		munk /= (1-mu);
		while (nn >= 1) {
			blend *= nn;
			nn--;
			if (kn > 1) {
				blend /= (double)kn;
				kn--;
			}
			if (nkn > 1) {
				blend /= (double)nkn;
				nkn--;
			}
		}
		result += data[k] * blend;
	}

	return (result);
}

公式是同一个公式,反正我没看懂。

猜你喜欢

转载自blog.csdn.net/hailler1119/article/details/89195558
今日推荐