Python011: Python大作业之移动的小火车动画(四)代码实现

书接上文:Python010: Python大作业之移动的小火车动画(三)结果显示

0.注意:

​ 该项目使用的库和资源说明如下:

pygame 2.0.1 (SDL 2.0.14, Python 3.6.6)

​ 另外还添加了一个字体如下图:
在这里插入图片描述

1.MyLuckyTrain.py
import time
import pygame
import CCarriage
from CCarriage import CarrigSize
from CCarriage import TrackSize
from CCarriage import Speed
from Common import one_get_three
from Common import Colors
from Common import dis_group_info

# ==================================================================================================
WINDOW_SIZE = (1500, 800)  # 定义窗口的宽、高
FONT_SIZE = 30  # 设置字体大小
CARGE_SIZE = CarrigSize.middle  # 确定车的大小,关系到点源的选取和车厢的绘制
TRACK_SIZE = TrackSize.large  # 指定车轨道的大小
START_POINT = (150, 750)  # 指定轨道的起点
TRAIN_SPEED = Speed.High_speed  # 指定小火车的速度
# ==================================================================================================
# 初始化
pygame.init()
# 创建窗口--大小
MyWindow = pygame.display.set_mode(WINDOW_SIZE)
# 设置窗口标题
pygame.display.set_caption("懂王唐王的小火车")
# 设置窗口背景为白色
MyWindow.fill(Colors.white)
# 刷新窗口
pygame.display.flip()
# 创建轨道对象
track = CCarriage.Tracks(MyWindow, TRACK_SIZE, START_POINT)
# 绘制轨道
track.draw_tracks()
# 获取起点的源列表
list_source = track.getstartlist()
# 获取最终的点集
fin_list = one_get_three(CARGE_SIZE * 0.6, CARGE_SIZE * 0.6, list_source)
list_len = len(fin_list)
# 刷新窗口--更新轨道
pygame.display.update()

# 将点集变成迭代器
it_train = iter(fin_list)
# 循环保持程序的一直运行
num = 0
g_bFlag = True
while g_bFlag:
    num += 1  # 用于防止迭代器穷尽后弹出异常
    # ======================动画帧的刷新=========================#
    if num < list_len:
        templist = next(it_train)
        MyWindow.fill(Colors.white)  # 重绘背景
        pygame.draw.rect(MyWindow, Colors.gray, (10, 10, WINDOW_SIZE[0] - 20, WINDOW_SIZE[1] - 20), 1)
        dis_group_info(MyWindow, "Course  Info: Python 期中大作业 ", "Group Member: 董照诚 唐佳玄 王凯 王雅婷 ", FONT_SIZE)
        track.draw_tracks()
        trainback = CCarriage.Carriages(MyWindow, CARGE_SIZE, templist[0], templist[1])  # 绘制小火车的后车厢
        hook1 = trainback.draw_carriages()
        trainford = CCarriage.Carriages(MyWindow, CARGE_SIZE, templist[2], templist[3])  # 绘制小火车的前车厢
        hook2 = trainford.draw_carriages()
        pygame.draw.line(MyWindow, Colors.yellow, hook1[1], hook2[0])
        pygame.display.update()

    time.sleep(TRAIN_SPEED)
    # 4.检测事件
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            g_bFlag = False

2.Common.py
import pygame
import math
from math import sin
from math import cos
from math import atan
from math import pi
from math import sqrt


# 颜色类Colors,画图时可直接选择颜色
class Colors:
    white = (255, 255, 255)
    black = (0, 0, 0)
    red = (255, 0, 0)
    green = (0, 255, 0)
    blue = (0, 0, 255)
    sky_blue = (28, 180, 240)
    font_blue = (71, 161, 250)
    yellow = (255, 190, 0)
    gray = (60, 63, 65)


# 功能 :给一个向量的起点和终点的坐标,求解该延长线上距离终点为r的点坐标,也即two_get_third所表达的含义
# 参数1:距离r
# 参数2:终点p1的坐标
# 参数3:起点p2的坐标
# 返回 :一个列表---包含了待求点和p2p1组成的向量的方向角(单位弧度)
def two_get_third(r=0.0, point1=(), point2=()):  # 注意:point1()为靠近待求点那一侧的点
    ret_list = [(0, 0), 0.0]
    if point1[0] - point2[0] == 0:  # 如果车轮两点在一条竖线上
        if point1[1] - point2[1] > 0:
            ret_list = [(point1[0], point2[1] + r), pi / 2]  # p1在p2的上面
        else:
            ret_list = [(point1[0], point2[1] - r), -pi / 2]  # p1在p2的下面
    else:  # 如果车轮两点不在一条竖线上
        if point1[0] < point2[0]:
            rad = atan((point1[1] - point2[1]) / (point1[0] - point2[0])) + pi
        else:
            rad = atan((point1[1] - point2[1]) / (point1[0] - point2[0]))
        vec_cos = cos(rad)  # 先算p2-->p1的向量的方向余弦
        vec_sin = sin(rad)  # 再算p2-->p1的向量的方向正弦
        ret_list = [(r * vec_cos + point1[0], r * vec_sin + point1[1]), rad]

    return ret_list


# 功能 :对于两节车厢,有4个车轮,也即是4个点记为P1、P2、P3、P4
#       该函数的功能即使,通过给定的P1,求得P1对应的P2、P3、P4
# 参数1:r1 车轮间距
# 参数2:r2 车厢连接处轮间距
# 参数3:由n多个P1组成的P1的点的列表
# 返回 :由n个形如[P1、P2、P3、P4]组成的列表
#
# 为什么这么做?
# 因为画车厢所有的源头都是来自于P1、P2、P3、P4这4个点
def one_get_three(r1, r2, my_list=[]):
    p_sour = my_list[0]
    accu = r1 / 12  # 精度整r1的1/12,也即车轮间距的1/12
    rev_len = math.floor((3 * r1 + 0.4 * r1) / my_list[1]) + 10  # 取点精度主要是来自于轨道设计处
    retril_range = 100
    p_start_list = p_sour[:(len(p_sour) - rev_len)]
    ret_plist = []
    for each in p_start_list:
        temp_list = [(), (), (), ()]
        # P1
        temp_list[0] = each

        # P2
        e_index = p_sour.index(each)
        if e_index > (len(p_sour) - retril_range - 1):
            e_index_e = len(p_sour) - 1
        else:
            e_index_e = e_index + retril_range
        it_1 = iter(p_sour[(e_index + 1):e_index_e])  # 创建一个迭代器,包含p_sour中each之后的所有元素
        temp_point_1 = next(it_1)
        flag_1 = sqrt((temp_point_1[0] - each[0]) ** 2 + (temp_point_1[1] - each[1]) ** 2) - r1  # 求得p1和p2'之间的距离
        while flag_1 < -accu or flag_1 > accu:
            temp_point_1 = next(it_1)
            flag_1 = sqrt((temp_point_1[0] - each[0]) ** 2 + (temp_point_1[1] - each[1]) ** 2) - r1
        temp_list[1] = temp_point_1

        # P3
        f_index = p_sour.index(temp_point_1)  # 求出p2的索引
        if f_index > (len(p_sour) - retril_range - 1):
            f_index_e = len(p_sour) - 1
        else:
            f_index_e = f_index + retril_range
        it_2 = iter(p_sour[(f_index + 1):f_index_e])
        temp_point_2 = next(it_2)
        flag_2 = sqrt((temp_point_2[0] - temp_point_1[0]) ** 2 + (temp_point_2[1] - temp_point_1[1]) ** 2) - r2
        while flag_2 < -accu or flag_2 > accu:
            temp_point_2 = next(it_2)
            flag_2 = sqrt((temp_point_2[0] - temp_point_1[0]) ** 2 + (temp_point_2[1] - temp_point_1[1]) ** 2) - r2
        temp_list[2] = temp_point_2

        # P4
        g_index = p_sour.index(temp_point_2)  # 求出p3的索引
        if g_index > (len(p_sour) - retril_range - 1):
            g_index_e = len(p_sour) - 1
        else:
            g_index_e = g_index + retril_range
        it_3 = iter(p_sour[(g_index + 1):g_index_e])
        temp_point_3 = next(it_3)
        flag_3 = sqrt((temp_point_3[0] - temp_point_2[0]) ** 2 + (temp_point_3[1] - temp_point_2[1]) ** 2) - r1
        while flag_3 < -accu or flag_3 > accu:
            temp_point_3 = next(it_3)
            flag_3 = sqrt((temp_point_3[0] - temp_point_2[0]) ** 2 + (temp_point_3[1] - temp_point_2[1]) ** 2) - r1
        temp_list[3] = temp_point_3

        ret_plist.append(temp_list)
    return ret_plist


def dis_group_info(wnd, str1="", str2="", size=30):
    myfont = pygame.font.Font('resource/yaheiconsolashybrid.ttf', size)
    text_course_info = myfont.render(str1, True, Colors.font_blue)
    text_meminfo = myfont.render(str2, True, Colors.font_blue)
    wnd.blit(text_course_info, (20, 20))  # 渲染文字
    wnd.blit(text_meminfo, (20, 20 + size + 10))  # 渲染文字

3.CCarrage.py
import pygame
from Common import two_get_third
from Common import Colors
from math import pi
from math import cos
from math import sin
import math


class Carriages:
    r1 = 0.0  # 同一车厢车钩与车轮的距离
    r2 = 0.0  # 两个车轮的间距

    # 车厢类的初始化函数
    def __init__(self, wnd, carge_size, p1=(), p2=()):
        self.myWindow = wnd  # 目标窗口
        self.carge_P1 = p1  # 后车厢的后车轮
        self.carge_P2 = p2  # 后车厢的前车轮
        # self.carge_P3 = p3  # 后车厢的挂钩
        self.carge_len = carge_size  # 车厢长度
        self.carge_wid = carge_size / 6  # 车的宽度      已知车轮间距x--->车宽为x/3
        self.r1 = carge_size * 0.1  # 同一车厢车钩与车轮的距离   已知车轮间距x--->车轮和车钩的距离为x/6
        self.r2 = carge_size * 0.6  # 两个车轮的间距 已知车轮间距x--->车长度为x/0.6

    # 绘制单节车厢
    def draw_carriages(self):
        # 先画车轮
        pygame.draw.circle(self.myWindow, Colors.black, self.carge_P1, self.carge_wid / 6, 0)  # 后车轮
        pygame.draw.circle(self.myWindow, Colors.black, self.carge_P2, self.carge_wid / 6, 0)  # 前车轮

        # 再画挂钩
        l_p3_list = two_get_third(self.r1, self.carge_P1, self.carge_P2)  # 返回一个列表,第一个元素为点
        pygame.draw.circle(self.myWindow, Colors.yellow, l_p3_list[0], self.carge_wid / 6, 0)  # 左挂钩
        l_p4_list = two_get_third(self.r1, self.carge_P2, self.carge_P1)
        pygame.draw.circle(self.myWindow, Colors.yellow, l_p4_list[0], self.carge_wid / 6, 0)  # 右挂钩

        # 再画车框---先求4个顶点---再用一条lines语句画折线
        # 求出左下方的点lb
        l_on_line_list = two_get_third(self.carge_len * 0.2, self.carge_P1, self.carge_P2)
        vec_coslb = cos(l_on_line_list[1] - pi / 2)
        vec_sinlb = sin(l_on_line_list[1] - pi / 2)
        p_on_linel = l_on_line_list[0]
        lb_point = (p_on_linel[0] + self.carge_wid / 2 * vec_coslb, p_on_linel[1] + self.carge_wid / 2 * vec_sinlb)
        # 求出左上方的点la
        vec_cosla = cos(l_on_line_list[1] + pi / 2)
        vec_sinla = sin(l_on_line_list[1] + pi / 2)
        la_point = (p_on_linel[0] + self.carge_wid / 2 * vec_cosla, p_on_linel[1] + self.carge_wid / 2 * vec_sinla)

        # 求出右下方的点rb
        r_on_line_list = two_get_third(self.carge_len * 0.2, self.carge_P2, self.carge_P1)
        vec_cosrb = cos(r_on_line_list[1] + pi / 2)
        vec_sinrb = sin(r_on_line_list[1] + pi / 2)
        p_on_liner = r_on_line_list[0]
        rb_point = (p_on_liner[0] + self.carge_wid / 2 * vec_cosrb, p_on_liner[1] + self.carge_wid / 2 * vec_sinrb)
        # 求出右上方的点ra
        vec_cosra = cos(r_on_line_list[1] - pi / 2)
        vec_sinra = sin(r_on_line_list[1] - pi / 2)
        ra_point = (p_on_liner[0] + self.carge_wid / 2 * vec_cosra, p_on_liner[1] + self.carge_wid / 2 * vec_sinra)
        # 绘制闭合折线--矩形
        point_list = [la_point, lb_point, rb_point, ra_point]
        pygame.draw.lines(self.myWindow, Colors.black, True, point_list, 1)

        return [l_p3_list[0], l_p4_list[0]]


class Tracks:
    __curv1_rad = 0.0  # 圆弧轨道1跨越的弧度
    __curv2_rad = 0.0  # 圆弧轨道2跨越的弧度

    startlist = []  # 起点的列表
    __l1_end_p = ()  # 直线轨道1的末端

    __cur1_start = 0.0  # 弧线轨道1的起始角
    __cur1_end_p = ()  # 弧线轨道1的末端点
    __o1_p = ()

    __vec_l2_cos = 0.0  # 直线轨道2的方向余弦
    __vec_l2_sin = 0.0  # 直线轨道2的方向正弦
    __l2_end_p = ()  # 直线轨道2的末端点

    __cur2_end = 0.0  # 弧线轨道2的终止角度(这是相对于draw.arc函数的终止),相对于弧线轨道,其实是起始角
    __o2_p = ()

    __l3_start_p = ()  # 直线轨道3的起点
    __l3_end_p = ()  # 直线轨道3的末端点

    # 轨道类的构造函数
    def __init__(self, wnd, tracksize=[(0, 0, 0), (0, 0), (0, 0)], start_p=(0.0, 0.0)):
        self.myWindow = wnd  # 目标窗口
        self.line1 = tracksize[0][0]  # lines[0]  # 直轨道1长度
        self.line2 = tracksize[0][1]  # lines[1]  # 直轨道2长度
        self.line3 = tracksize[0][2]  # lines[2]  # 直轨道3长度
        self.curv1_R1 = tracksize[1][0]  # curves1[0]  # 圆弧轨道1的半径
        self.curv1_M1 = tracksize[1][1]  # curves1[1]  # 圆弧轨道1的弧长
        self.curv2_R2 = tracksize[2][0]  # 圆弧轨道2的半径
        self.curv2_M2 = tracksize[2][1]  # curves2[1]  # 圆弧轨道2的弧长
        self.start_point = start_p  # 轨道的起点
        self.__to_rad()

    # 求出圆弧轨道的弧度
    def __to_rad(self):
        self.__curv1_rad = self.curv1_M1 / self.curv1_R1
        self.__curv2_rad = self.curv2_M2 / self.curv2_R2

    # 绘制车道
    def draw_tracks(self):
        width = 1
        # ==================================================================================================================
        # 第一段直线
        l1_end_p = (self.start_point[0] + self.line1, self.start_point[1])
        pygame.draw.line(self.myWindow, Colors.red, self.start_point, l1_end_p, width)
        self.__l1_end_p = l1_end_p
        # ==================================================================================================================
        # 第一段圆弧
        # 1.求出pos
        # 2.确定起始角度
        # 3.确定终止角度
        # 4.画弧
        # 如果添加一个偏移量(试了一下加一个像素就会好很多),图片显示会更加精确一点
        rect_pos_c1 = (l1_end_p[0] - self.curv1_R1, l1_end_p[1] - 2 * self.curv1_R1, 2 * self.curv1_R1, 2 * self.curv1_R1)
        # delta = math.radians(2)
        cur1_start = -pi / 2
        cur1_end = cur1_start + self.__curv1_rad
        cur1_end_re = cur1_end + math.radians(3)  # 添加一个修正角度,使其连接在一起
        pygame.draw.arc(self.myWindow, Colors.sky_blue, rect_pos_c1, cur1_start, cur1_end_re, width)
        self.__cur1_start = -cur1_start
        # ==================================================================================================================
        # 第二段直线
        # 1.求出第一段圆弧的圆心
        # 2.求出O1P1向量的方向角(方向角与画图时的角度是不一样的,关于x轴镜像)
        # 3.求出P1--cur1_end_p点
        # 4.偏移90°得到第二段直线的方向角
        # 5.有了方向就有了方向余弦和方向正弦
        # 6.结合line2的长度求出line2的终点
        # 7.划线即可
        o1_p = (l1_end_p[0], l1_end_p[1] - self.curv1_R1)
        vec_rad_o1p1 = -cur1_end
        vec_cos = cos(vec_rad_o1p1)  # 方向余弦
        vec_sin = sin(vec_rad_o1p1)  # 方向正弦
        cur1_end_p = (o1_p[0] + self.curv1_R1 * vec_cos, o1_p[1] + self.curv1_R1 * vec_sin)
        self.__cur1_end_p = cur1_end_p
        self.__o1_p = o1_p
        vec_l2_rad = vec_rad_o1p1 - pi / 2
        vec_l2_cos = cos(vec_l2_rad)
        vec_l2_sin = sin(vec_l2_rad)
        l2_end_p = (cur1_end_p[0] + self.line2 * vec_l2_cos, cur1_end_p[1] + self.line2 * vec_l2_sin)
        pygame.draw.line(self.myWindow, Colors.red, cur1_end_p, l2_end_p, width)
        self.__vec_l2_cos = vec_l2_cos
        self.__vec_l2_sin = vec_l2_sin
        self.__l2_end_p = l2_end_p
        # ==================================================================================================================
        # 画第二个圆弧
        # 1.求出P2O2向量的方向角(其中圆弧1和圆弧2在第二段直线的两端处的方向是平行)
        # 2.根据已知的l2_end_p求得圆心O2的坐标
        # 3.根据圆心坐标和圆弧的半径求得pos
        # 4.求出圆弧的终止弧度:因为l3直线是水平,所以start弧度已知,减去圆弧角得end弧度
        # 5.起始弧度、终止弧度、pos均已知,调用draw画弧即可
        vec_rad_p2o2 = vec_rad_o1p1
        vec_cos = cos(vec_rad_p2o2)  # 方向余弦
        vec_sin = sin(vec_rad_p2o2)  # 方向正弦
        o2_p = (l2_end_p[0] + self.curv2_R2 * vec_cos, l2_end_p[1] + self.curv2_R2 * vec_sin)
        rect_pos_c2 = (o2_p[0] - self.curv2_R2, o2_p[1] - self.curv2_R2, self.curv2_R2 * 2, self.curv2_R2 * 2)
        cur2_start = pi / 2
        cur2_end = cur2_start + self.__curv2_rad
        cur2_end_re = cur2_end + math.radians(1)  # 修正用的结束角度
        pygame.draw.arc(self.myWindow, Colors.sky_blue, rect_pos_c2, cur2_start, cur2_end_re, width)
        self.__cur2_end = -cur2_end
        self.__o2_p = o2_p
        # ==================================================================================================================
        # 画第三段直线
        l3_start_p = (o2_p[0], o2_p[1] - self.curv2_R2)
        l3_end_p = (l3_start_p[0] + self.line3, l3_start_p[1])
        pygame.draw.line(self.myWindow, Colors.red, l3_start_p, l3_end_p, width)
        self.__l3_start_p = l3_start_p
        self.__l3_end_p = l3_end_p

    def getstartlist(self):
        if 10 < self.line1 <= 200:  # 轨道长度不应该太短至少大于10个像素,另外最适宜是200到400之间
            step = 2
        elif 200 < self.line1 <= 400:
            step = 3
        elif self.line1 >= 400:
            step = 4

        # 存储第一段直线上的点源=====================================================================
        points_a = self.start_point
        while self.__l1_end_p[0] - points_a[0] > 2:
            self.startlist.append(points_a)
            points_a = (points_a[0] + step, points_a[1])
        # 存储第一段圆弧上的点源=========================================================================
        points_b = self.__l1_end_p
        start_rad = self.__cur1_start
        delta = math.radians(1)
        while (self.__cur1_end_p[0] - points_b[0]) ** 2 + (self.__cur1_end_p[1] - points_b[1]) ** 2 > 8:
            self.startlist.append(points_b)
            start_rad = start_rad - delta
            cos_temp = cos(start_rad)
            sin_temp = sin(start_rad)
            points_b = (self.__o1_p[0] + self.curv1_R1 * cos_temp, self.__o1_p[1] + self.curv1_R1 * sin_temp)
        # 存储第二段直线的点源==========================================================================
        deltax = 4 * self.__vec_l2_cos
        deltay = 4 * self.__vec_l2_sin
        points_c = self.__cur1_end_p
        while (points_c[1] - self.__l2_end_p[1]) > 2:
            self.startlist.append(points_c)
            points_c = (points_c[0] + deltax, points_c[1] + deltay)
        # 存储第二段圆弧上的点源=========================================================================
        points_d = self.__l2_end_p
        start_rad = self.__cur2_end
        delta = math.radians(1)
        while (self.__l3_start_p[0] - points_d[0]) ** 2 + (self.__l3_start_p[1] - points_d[1]) ** 2 > 8:
            self.startlist.append(points_d)
            start_rad = start_rad + delta
            cos_temp = cos(start_rad)
            sin_temp = sin(start_rad)
            points_d = (self.__o2_p[0] + self.curv2_R2 * cos_temp, self.__o2_p[1] + self.curv2_R2 * sin_temp)

        # 存储第三段直线上的点源=====================================================================
        points_e = self.__l3_start_p
        while self.__l3_end_p[0] - points_e[0] > 2:
            self.startlist.append(points_e)
            points_e = (points_e[0] + step, points_e[1])

        return [self.startlist, step]


class CarrigSize:
    small = 50  # 长50宽10
    middle = 60  # 长60 宽12
    large = 75  # 长75 宽15
    Xlarge = 90  # 长90 宽18


class TrackSize:
    small = [(190, 150, 200), (150, 210), (150, 210)]  # [(l1, l2, l3), (r1, m1) (r2, m2)]
    middle = [(350, 200, 350), (180, 250), (180, 250)]
    large = [(400, 250, 400), (200, 280), (200, 280)]


class Speed:
    oxcart = 0.1
    normal_speed = 0.04
    High_speed = 0.01
    plane = 0.002

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咖啡与乌龙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值