C语言学习记录20240622

这次需要用 C 语言库 Allegro 写爆破彗星游戏。项目有一些描述如需要绘制飞船、彗星、子弹,需要响应按键实现飞船加速、减速、转向、开火,需要绘制弹道,需要实现彗星旋转、缩放,需要碰撞检测,需要显示计分。

这些用 wxPython 不难实现,因为有面向对象能帮我们很好地组织代码,甚至能在写代码前先画好类图。但在 C 语言里,没有类实现,难道靠注释约定函数们谁跟谁负责什么功能。

模糊的印象,类是用来描述具有相同属性和方法的一组事物的数据结构。在 Python 中,没有属性,可以只是函数;没有方法,可以是命名元组或字典;有存储变量和一个函数,可以是闭包。
没有属性可以是类吗?好像也行,比如写个通用的工具类,里面放一些常用的日志、计算之类的只和类绑定的静态方法。
没有方法可以是类吗?也可以,避免使用全局变量,写一个类专门存放只与类绑定的全局变量,然后通过类来调用。
闭包是类吗?它不能继承,也不能重写闭包里的函数。
最后似乎可以通过这些简单逻辑粗略得出结论,类是封装、继承、多态这三个面向对象概念的具体实现。

C 语言虽然没有直接的类实现,但可以通过结构体封装属性变量和方法,结构体也能通过嵌套实现继承效果,至于多态,指针是不是更自由。

这里 C 语言实现封装,我见过的一种是结构体中的方法用函数指针表示,还有一种是结构体模拟类只负责保存属性,方法函数单独写,但函数必须有一个参数是指向模拟类结构体变量的指针。

再看《嗨翻C语言》第 535 页的void draw_ship(Spaceship* s),应该是第二种方法,这样的话先按这种方式写出飞船类。

对引用和指针也很困惑,直接查看《引用与指针的区别》https://blog.csdn.net/HUAERBUSHI521/article/details/118368696

在其他模块中调用全局变量,需要使用 extern 声明,表明是调用外部变量。

多个模块怎么避免重复包含头文件?在头文件中使用预处理#ifndef #define #end,通过判断是否定义指定变量来跳过其他代码,有点像 Python 中判断if __name__ == "__main__"

进展缓慢,只写了飞船的显示部分,写的时候查漏补缺挺好。

C语言学习记录20240622
main.c

#include <stdio.h>
#include <allegro5/allegro.h>
#include <allegro5/allegro_primitives.h>
#include <allegro5/allegro_font.h>
#include <allegro5/allegro_ttf.h>
#include "./utility.h"
#include "./spaceship.h"


// 全局变量 
const int WIDTH = 800; // 屏幕尺寸 
const int HEIGHT = 600;
const int FPS = 60; // 帧率 
ALLEGRO_FONT *font_40; // 字体 

int main() {
    // 初始化 Allegro
    if (!al_init()) {
        fprintf(stderr, "Failed to initialize Allegro!\n");
        return -1;
    }

    // 安装键盘驱动,安装成功或已经安装过则返回 true
    if (!al_install_keyboard()) {
        fprintf(stderr, "Failed to initialize keyboard!\n");
        return -1;
    }

    // 初始化图形绘制插件 
    if (!al_init_primitives_addon()) {
        fprintf(stderr, "Failed to initialize primitives addon!\n");
        return -1;
    }
    
    // 初始化字体插件 
    if (!al_init_font_addon()) {
        fprintf(stderr, "Failed to initialize font addon!\n");
        return -1;
    }
    
    // 初始化 TTF 字体插件 
    if (!al_init_ttf_addon()) {
        fprintf(stderr, "Failed to initialize ttf font addon!\n");
        return -1;
    }
    // 加载 TTF 字体,字号 40 
	font_40 = al_load_ttf_font("arial.ttf", 40, 0);
	
    // 启用多重采样
    al_set_new_display_option(ALLEGRO_SAMPLE_BUFFERS, 1, ALLEGRO_SUGGEST);
    al_set_new_display_option(ALLEGRO_SAMPLES, 4, ALLEGRO_SUGGEST);
    
    // 创建指定宽高的窗口 
    ALLEGRO_DISPLAY *display = al_create_display(WIDTH, HEIGHT);
    if (!display) {
        fprintf(stderr, "Failed to create display!\n");
        return -1;
    }

    // 创建事件队列 
    ALLEGRO_EVENT_QUEUE *event_queue = al_create_event_queue();
    if (!event_queue) {
        fprintf(stderr, "Failed to create event queue!\n");
        al_destroy_display(display);
        return -1;
    }
    
    // 创建定时器,每 1.0 / FPS 秒触发一次 
    ALLEGRO_TIMER *timer = al_create_timer(1.0 / FPS);

    // 注册事件源到事件队列 
    al_register_event_source(event_queue, al_get_display_event_source(display));
    al_register_event_source(event_queue, al_get_keyboard_event_source());
    al_register_event_source(event_queue, al_get_timer_event_source(timer));

    // 清除屏幕并填充黑色 
	al_clear_to_color(al_map_rgb(0, 0, 0));
	
	// 绘制背景 logo 
	draw_logo();
		
	// 初始化并绘制飞船 
	Spaceship s = {WIDTH / 2.0, HEIGHT / 2.0, 0.0, 3.0, 0, al_map_rgb(0, 255, 0)};
	draw_ship(&s);
	
	// 交换缓冲区
    al_flip_display();
    
    // 启动定时器 
    al_start_timer(timer);
    
    // 轮询事件 
    bool done = false;
	while (!done) {
        ALLEGRO_EVENT event;
        // 等待从事件队列取出事件 
        al_wait_for_event(event_queue, &event);
        
        // 处理按键事件,这里响应 ESC 按键 
		if (event.type == ALLEGRO_EVENT_KEY_DOWN && event.keyboard.keycode == ALLEGRO_KEY_ESCAPE) {
        	done = true;
        }

        // 处理窗口事件,这里响应点击窗口右上角关闭 
		if (event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
            done = true;
        }
        
        // 处理定时器事件 
        if (event.type == ALLEGRO_EVENT_TIMER) {
        	// 清屏并更新绘制
	        al_clear_to_color(al_map_rgb(0, 0, 0));
	        draw_logo();
	        // 每次更新转动 5 度 
	        s.heading += 5.0; // s.heading 相当于 (&s)->heading
	        draw_ship(&s);
	        al_flip_display();
        } 
    }

    // 销毁资源,释放内存 
    al_destroy_timer(timer);
    al_destroy_font(font_40);
    al_destroy_display(display);
    al_destroy_event_queue(event_queue);

    return 0;
}


spaceship.h

#ifndef _SPACESHIP_H
#define _SPACESHIP_H

#include <allegro5/allegro.h>


typedef struct {
	float sx; // 飞船中屏幕中的坐标 
	float sy;
	float heading; //飞船朝向角度,如 30 度为 30.0 
	float speed;
	int gone; // 是否阵亡
	ALLEGRO_COLOR color; 
} Spaceship;

void draw_ship(Spaceship*);

#endif

spaceship.c

#include <allegro5/allegro_primitives.h>
#include "./spaceship.h"
#define DEGREES(x) ((x) * ALLEGRO_PI / 180.0)


// 绘制飞船
void draw_ship(Spaceship* s) {
	ALLEGRO_TRANSFORM transform;
	al_identity_transform(&transform);
	al_rotate_transform(&transform, DEGREES(s->heading));
	al_translate_transform(&transform, s->sx, s->sy);
	al_use_transform(&transform);
	// 画线需要在调用 al_create_display 前设置多重采样以抗锯齿 
	al_draw_line(-8, 9, 0, -11, s->color, 3.0f);
	al_draw_line(0, -11, 8, 9, s->color, 3.0f);
	al_draw_line(-6, 4, -1, 4, s->color, 3.0f);
	al_draw_line(6, 4, 1, 4, s->color, 3.0f);
	// 重置变换矩阵,不然会影响其他绘制内容 
    al_identity_transform(&transform);
    al_use_transform(&transform);
}

utility.h

#ifndef _UTILITY_H
#define _UTILITY_H

extern const int WIDTH;
extern const int HEIGHT;
extern ALLEGRO_FONT *font_40;

void draw_logo();

#endif

utility.c

#include <allegro5/allegro.h>
#include <allegro5/allegro_primitives.h>
#include <allegro5/allegro_font.h>
#include "./utility.h"


void draw_logo() {
    const char *text = "SmileBasic";
    // 获取指定字体的字符串外边框尺寸 
    int bbx, bby, bbw, bbh;
    al_get_text_dimensions(font_40, text, &bbx, &bby, &bbw, &bbh);
    // 居中绘制字符串,纵轴偏上显示 
	al_draw_text(font_40, al_map_rgb(255, 255, 255), WIDTH / 2.0, HEIGHT / 2.0 - bbh * 1.5, ALLEGRO_ALIGN_CENTRE, text);
	
	// 绘制红色矩形边框,四周设置边距 10 
	float padding = 10.0;
	float rect_x1 = (WIDTH - bbw) / 2.0 - padding;
	float rect_y1 = HEIGHT / 2.0 - bbh * 1.5 - padding;
	float rect_x2 = rect_x1 + bbw + padding * 2;
	float rect_y2 = rect_y1 + bbh * 1.5 + padding * 2;
	al_draw_rectangle(rect_x1, rect_y1, rect_x2, rect_y2, al_map_rgb(255, 0, 0), 2.0);
}

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值