193 lines
6.1 KiB
Markdown
193 lines
6.1 KiB
Markdown
Вот доработанная версия с **общими переменными между основной программой и модулями**. Мы создадим единую структуру `GameState`, которая будет передаваться в каждый модуль.
|
||
|
||
---
|
||
|
||
### **1. Общий заголовок (SHARED.H)**
|
||
```c
|
||
// Структура для общих переменных игры
|
||
typedef struct {
|
||
int lives; // Количество жизней
|
||
int level; // Текущий уровень
|
||
bool is_paused; // Состояние паузы
|
||
uint32_t score; // Счёт игрока
|
||
} GameState;
|
||
|
||
// Заголовок модуля
|
||
typedef void (*ModuleEntry)(GameState*); // Тип функции-входа
|
||
|
||
typedef struct {
|
||
uint32_t magic; // 0x55AA1234
|
||
ModuleEntry entry; // Функция входа
|
||
uint32_t size; // Размер модуля
|
||
} ModuleHeader;
|
||
```
|
||
|
||
---
|
||
|
||
### **2. Основная программа (MAIN.C)**
|
||
```c
|
||
#include <libetc.h>
|
||
#include <libcd.h>
|
||
#include "SHARED.H"
|
||
|
||
#define MODULE_ADDR 0x80030000 // Адрес загрузки модулей
|
||
|
||
GameState game_state = {
|
||
.lives = 3,
|
||
.level = 1,
|
||
.is_paused = false,
|
||
.score = 0
|
||
};
|
||
|
||
void load_and_run_module(int sector, int size) {
|
||
CdRead(sector, (uint32_t*)MODULE_ADDR, size);
|
||
CdReadSync(0);
|
||
|
||
ModuleHeader *header = (ModuleHeader*)MODULE_ADDR;
|
||
if (header->magic == 0x55AA1234) {
|
||
header->entry(&game_state); // Передаём указатель на game_state
|
||
}
|
||
}
|
||
|
||
int main() {
|
||
ResetGraph(0);
|
||
CdInit();
|
||
FntLoad(960, 256);
|
||
FntOpen(16, 16, 256, 224, 0, 512);
|
||
|
||
while (1) {
|
||
// Обновляем общие переменные
|
||
game_state.score += 10;
|
||
FntPrint("Main: Lives=%d, Score=%d\n", game_state.lives, game_state.score);
|
||
|
||
// Загружаем и выполняем модули
|
||
load_and_run_module(MODULE_MDEC_SECTOR, MODULE_MDEC_SIZE);
|
||
load_and_run_module(MODULE_GPU_SECTOR, MODULE_GPU_SIZE);
|
||
|
||
VSync(0);
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### **3. Модуль для MDEC (MODULE_MDEC.C)**
|
||
```c
|
||
#include <libmdec.h>
|
||
#include "SHARED.H"
|
||
|
||
void module_entry(GameState *state) {
|
||
// Читаем и изменяем общие переменные
|
||
if (state->lives > 0) {
|
||
state->lives--; // Уменьшаем жизни
|
||
FntPrint("MDEC: Lives left=%d\n", state->lives);
|
||
}
|
||
|
||
// Декодируем видео только если игра не на паузе
|
||
if (!state->is_paused) {
|
||
uint8_t data[1024];
|
||
uint16_t buffer[256*256];
|
||
MDEC_DecodeFrame(data, buffer, MDEC_MODE_RGB24);
|
||
}
|
||
}
|
||
|
||
ModuleHeader __header__ __attribute__((section(".header"))) = {
|
||
.magic = 0x55AA1234,
|
||
.entry = module_entry,
|
||
.size = 0
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
### **4. Модуль для GPU (MODULE_GPU.C)**
|
||
```c
|
||
#include <libgpu.h>
|
||
#include "SHARED.H"
|
||
|
||
void module_entry(GameState *state) {
|
||
// Проверяем глобальный статус
|
||
if (state->is_paused) {
|
||
FntPrint("GPU: Game is paused!\n");
|
||
return;
|
||
}
|
||
|
||
// Рисуем HUD с общими переменными
|
||
char hud_text[32];
|
||
sprintf(hud_text, "LIVES: %d SCORE: %d", state->lives, state->score);
|
||
FntPrint(hud_text);
|
||
|
||
// Пример работы с GPU
|
||
RECT rect = {10, 10, 100, 100};
|
||
uint16_t color = state->lives > 1 ? 0xFF00 : 0xFF0000; // Красный если 1 жизнь
|
||
DrawPrim(&rect);
|
||
}
|
||
|
||
ModuleHeader __header__ __attribute__((section(".header"))) = {
|
||
.magic = 0x55AA1234,
|
||
.entry = module_entry,
|
||
.size = 0
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
### **5. Сборка**
|
||
```bash
|
||
# Компиляция модулей с их библиотеками
|
||
ccpsx MODULE_MDEC.C -o MODULE_MDEC.BIN -lpsxmdec
|
||
ccpsx MODULE_GPU.C -o MODULE_GPU.BIN -lpsxgpu
|
||
|
||
# Основная программа (без лишних библиотек)
|
||
ccpsx MAIN.C -o MAIN.EXE
|
||
```
|
||
|
||
---
|
||
|
||
### **6. Механика общих переменных**
|
||
1. **Основная программа**:
|
||
- Создаёт глобальную структуру `GameState`.
|
||
- Передаёт **указатель** на неё в модули через `header->entry(&game_state)`.
|
||
|
||
2. **Модули**:
|
||
- Читают и изменяют `GameState` через полученный указатель.
|
||
- Могут проверять:
|
||
```c
|
||
if (state->is_paused) { ... }
|
||
```
|
||
- Могут изменять:
|
||
```c
|
||
state->lives--;
|
||
```
|
||
|
||
3. **Синхронизация**:
|
||
- Все изменения мгновенно видны в основном модуле и других модулях.
|
||
|
||
---
|
||
|
||
### **7. Пример работы**
|
||
1. Основная программа:
|
||
- Устанавливает `lives = 3`, `score = 0`.
|
||
2. Модуль MDEC:
|
||
- Уменьшает `lives` до 2, если игрок ошибся.
|
||
3. Модуль GPU:
|
||
- Отображает текущие `lives` и `score` на экране.
|
||
4. Основная программа:
|
||
- Видит изменения и может реагировать (например, завершить игру при `lives == 0`).
|
||
|
||
---
|
||
|
||
### **8. Важные нюансы**
|
||
- **Безопасность**: Модули могут **перезаписать любые данные** в `GameState`. Добавьте проверки:
|
||
```c
|
||
if (state->lives < 0) state->lives = 0; // Защита от отрицательных значений
|
||
```
|
||
- **Производительность**: Частый доступ к общим переменным может замедлить работу. Оптимизируйте:
|
||
```c
|
||
int local_lives = state->lives; // Кэширование в локальную переменную
|
||
```
|
||
- **Конфликты**: Если модули работают с одними данными, добавьте **флаги блокировки**.
|
||
|
||
---
|
||
|
||
Теперь модули могут **полноценно взаимодействовать** с основной программой, проверяя и изменяя глобальное состояние игры! 🎮 |