这一篇是关于设置蛇的属性的,接上一篇(五)。
设置蛇的速度,很简单,只要不是负数就行了。
void SNK_SetSnakeSpeed(Snake *snake, int speed) { if (snake != 0) snake->speed = SDL_abs(speed); }
设置蛇的方向有些复杂,玩过贪吃蛇的都知道,蛇向前移动时,它无法向后转弯;向左移动时,它无法向右转弯。所以,我也要做些这样的判断。
void SNK_SetSnakeDirection(Snake *snake, int direction) { if (snake != 0 && (direction & (SNAKE_UP | SNAKE_DOWN | SNAKE_LEFT | SNAKE_RIGHT))) { if (snake->direction & (SNAKE_UP | SNAKE_DOWN)) { if (direction & (SNAKE_LEFT | SNAKE_RIGHT)) snake->direction = direction; } else { if (direction & (SNAKE_UP | SNAKE_DOWN)) snake->direction = direction; } } }
这样就能保证方向是对的,而且具有正确的转弯行为。
最后设置蛇的颜色,这个就没什么好说的了。
void SNK_SetSnakeColor(Snake *snake, Uint8 r, Uint8 g, Uint8 b, Uint8 a) { if (snake != 0) { snake->color.r = r; snake->color.g = g; snake->color.b = b; snake->color.a = a; } }
到此,蛇的实现代码就写完了。整个游戏最核心的部分也就这些了,接下来就是弄个美观的界面了。
以下是snk-snake.c文件的完整源码:
#include "snk-snake.h" #define INIT_SNAKE(world, size, x, y) \ snake->world = (world); \ snake->x = (SDL_abs(x) > (world)->w) ? 0 : SDL_abs(x); \ snake->x = (size) ? ((snake->x / SDL_abs(size)) * SDL_abs(size)) : 0; \ snake->y = (SDL_abs(y) > (world)->h) ? 0 : SDL_abs(y); \ snake->y = (size) ? ((snake->y / SDL_abs(size)) * SDL_abs(size)) : 0; \ snake->size = (size) ? SDL_abs(size) : 0; \ snake->color.r = snake->color.g = snake->color.b = snake->color.a = 0; \ snake->speed = 0; \ snake->length = 1; \ snake->direction = SNAKE_UP; \ snake->body = 0; #define MOVE_SNAKE(body) do { \ switch ((body)->direction) \ { \ case SNAKE_UP: (body)->y -= snake->size; break; \ case SNAKE_DOWN: (body)->y += snake->size; break; \ case SNAKE_LEFT: (body)->x -= snake->size; break; \ case SNAKE_RIGHT:(body)->x += snake->size; break; \ } \ } while (0) #define APPEND_BODY(last, body) do { \ if ((last)->direction & (SNAKE_UP | SNAKE_DOWN)) { \ (body)->x = (last)->x; \ if ((last)->direction & SNAKE_UP) \ (body)->y = (last)->y + snake->size; \ else \ (body)->y = (last)->y - snake->size; \ } else { \ if ((last)->direction & SNAKE_LEFT) \ (body)->x = (last)->x + snake->size; \ else \ (body)->x = (last)->x - snake->size; \ (body)->y = (last)->y; \ } \ (body)->direction = (last)->direction; \ (body)->next = (snake->body != 0) ? snake->body : 0; \ snake->body = (body); \ ++snake->length; \ } while (0) #define REMOVE_BODY(body) do { \ snake->body = (body)->next; \ SDL_free(body); \ (body) = snake->body; \ } while (body) Snake * SNK_CreateSnake(World *world, int size, int x, int y) { Snake *snake; if (world == 0) return 0; if ((snake = (Snake *)SDL_malloc(sizeof(Snake))) == 0) return 0; INIT_SNAKE(world, size, x, y); SNK_GrowSnake(snake); return snake; } void SNK_DestroySnake(Snake *snake) { struct Body *body; if (snake != 0) { if ((body = snake->body)) REMOVE_BODY(body); SDL_free(snake); snake = 0; } } void SNK_MoveSnake(Snake *snake) { struct Body *body; if (snake != 0) { MOVE_SNAKE(snake); for (body = snake->body; body; body = body->next) { MOVE_SNAKE(body); body->direction = (body->next != 0) ? body->next->direction : snake->direction; } } } void SNK_DrawSnake(Snake *snake) { SDL_Rect rect; struct Body *body; if (snake != 0) { rect.x = snake->x; rect.y = snake->y; rect.w = rect.h = snake->size; if (((snake->world != 0) ? (snake->world->render != 0) : 0)) { SDL_SetRenderDrawColor(snake->world->render, snake->color.r, snake->color.g, snake->color.b, snake->color.a); SDL_RenderDrawRect(snake->world->render, &rect); for (body = snake->body; body; body = body->next) { rect.x = body->x; rect.y = body->y; SDL_RenderDrawRect(snake->world->render, &rect); } } } } void SNK_GrowSnake(Snake *snake) { struct Body *body; if (snake != 0) { if ((body = (struct Body *)SDL_malloc(sizeof(struct Body))) == 0) return; if (snake->body == 0) { APPEND_BODY(snake, body); } else { APPEND_BODY(snake->body, body); } } } int SNK_HasIntersection(Snake *snake, SDL_Rect rect) { SDL_Rect bodyrect; struct Body *body; if (snake != 0) { bodyrect.w = bodyrect.h = snake->size; for (body = snake->body; body; body = body->next) { bodyrect.x = body->x; bodyrect.y = body->y; if (SDL_HasIntersection(&bodyrect, &rect) != 0) return 1; } } return 0; } int SNK_GetSnakeStatus(Snake *snake) { SDL_Rect headrect; if (((snake != 0) ? (snake->world != 0) : 0)) { headrect.w = (snake->x > 0 && snake->x < snake->world->w); headrect.h = (snake->y > 0 && snake->y < snake->world->h); if (headrect.w && headrect.h) { headrect.x = snake->x; headrect.y = snake->y; headrect.w = headrect.h = snake->size; if (SNK_HasIntersection(snake, headrect) != 0) return SNAKE_DIED; return SNAKE_MOVABLE; } else { switch (snake->direction) { case SNAKE_UP: headrect.x = (snake->y > 0); break; case SNAKE_DOWN: headrect.x = ((snake->y + snake->size) < snake->world->h); break; case SNAKE_LEFT: headrect.x = (snake->x > 0); break; case SNAKE_RIGHT: headrect.x = ((snake->x + snake->size) < snake->world->w); break; } return ((headrect.x != 0) ? SNAKE_MOVABLE : 0); } } return 0; } void SNK_SetSnakeSpeed(Snake *snake, int speed) { if (snake != 0) snake->speed = SDL_abs(speed); } void SNK_SetSnakeDirection(Snake *snake, int direction) { if (snake != 0 && (direction & (SNAKE_UP | SNAKE_DOWN | SNAKE_LEFT | SNAKE_RIGHT))) { if (snake->direction & (SNAKE_UP | SNAKE_DOWN)) { if (direction & (SNAKE_LEFT | SNAKE_RIGHT)) snake->direction = direction; } else { if (direction & (SNAKE_UP | SNAKE_DOWN)) snake->direction = direction; } } } void SNK_SetSnakeColor(Snake *snake, Uint8 r, Uint8 g, Uint8 b, Uint8 a) { if (snake != 0) { snake->color.r = r; snake->color.g = g; snake->color.b = b; snake->color.a = a; } }
宏INIT_SNAKE中snake->x和snake->y重复了两次,主要是为了将蛇的位置和蛇的大小对齐。
APPEND_BODY用于追加链表节点,并设置snake->body永远指向蛇尾,提高追加节点的效率。