该项目gitee地址:https://gitee.com/jflyfox/GameSnake.git
一、游戏概述
贪吃蛇是一款经典的街机游戏,玩家通过控制蛇的移动方向,使其吃到食物并不断增长身体长度,同时避免撞到墙壁或自身身体。本设计文档描述了一个使用JavaFX框架开发的贪吃蛇游戏的设计思路和实现细节。
二、游戏功能需求
- 游戏初始化
- 游戏窗口具有固定大小(可配置)。
- 蛇、食物和游戏信息在游戏开始时进行初始化并正确显示。
- 蛇的移动
- 玩家可以使用方向键控制蛇的移动方向(上、下、左、右)。
- 蛇以固定速度移动,每次移动一个单位长度。
- 蛇不能直接掉头,必须先移动到下一个最小单位长度后才能改变方向。
- 食物生成
- 食物随机出现在游戏场景中的空白位置,且位置是最小单位长度的整数倍。
- 当蛇吃到食物后,食物消失并在新的位置重新生成。
- 身体增长
- 每当蛇吃到食物,蛇的身体长度增加一个单位。
- 碰撞检测
- 检测蛇头与墙壁的碰撞,若碰撞则蛇死亡,生命值减1,并重新初始化蛇的位置和身体长度(若生命值大于0)。
- 检测蛇头与自身身体的碰撞,若碰撞则蛇死亡,生命值减1,并重新初始化蛇的位置和身体长度(若生命值大于0)。
- 游戏状态管理
- 游戏具有开始、暂停、继续和结束等状态。
- 在游戏过程中可以通过按键暂停和继续游戏。
- 当蛇的生命值为0时,游戏结束,显示最终得分。
- 游戏信息显示
- 在游戏界面上显示蛇的生命值、当前得分以及操作提示(如暂停键、重新开始键)。
三、系统架构设计
(一)总体架构
游戏采用MVC(Model-View-Controller)架构模式的变体,主要由以下几个模块组成:
- 模型(Model)
- 包含游戏中的所有实体对象,如蛇(
Snake
)、蛇的身体(SnakeBody
)和食物(Food
)等。这些对象负责维护自身的状态和行为逻辑。
- 包含游戏中的所有实体对象,如蛇(
- 视图(View)
- 由JavaFX的图形界面组件构成,包括游戏场景(
GameScreen
)和信息显示(Information
)等部分。视图负责将模型中的数据以可视化的方式呈现给玩家。
- 由JavaFX的图形界面组件构成,包括游戏场景(
- 控制(Controller)
- 主要通过事件处理机制实现,包括键盘事件和鼠标事件的处理。控制模块负责接收玩家的输入,并根据输入更新模型和视图的状态。
(二)核心类设计
1. FFApplication
类
- 类的职责
- 作为整个应用程序的基础框架类,继承自
Application
,负责游戏窗口的创建和初始化过程。它提供了一个基本的模板,供子类实现具体的游戏逻辑。
- 作为整个应用程序的基础框架类,继承自
- 主要方法
start(Stage primaryStage)
:应用程序的启动入口点。在该方法中,首先调用before()
方法进行一些前置初始化操作,然后创建Group
和Scene
对象,接着调用after()
方法进行后续的初始化操作,最后将Scene
设置到Stage
并显示出来。before()
和after()
:这两个方法是留给子类重写的。before()
方法通常用于设置游戏窗口的大小等初始化操作;after()
方法用于在Scene
和Group
创建之后,进行一些添加游戏组件、注册事件监听器等操作。showStage(Stage stage)
:设置Stage
的场景并显示。同时提供了getScene()
和getRoot()
方法,用于获取场景和根节点。
2. FFContants
类
- 类的职责
- 作为一个常量类,定义了游戏中使用的各种全局常量,如游戏屏幕的大小、最小单位长度等。这些常量在整个游戏中被广泛使用,通过集中定义可以方便地进行修改和维护。
- 主要常量
WIDTH
和HEIGHT
:定义了游戏屏幕的宽度和高度,默认值分别为800和600像素。MIN_X
和MIN_Y
:表示游戏中的最小单位长度,在水平和垂直方向上的默认值均为20像素。同时提供了init(int width, int height)
方法,用于重新初始化屏幕大小的常量。
3. FFEvent
类
- 类的职责
- 定义了游戏中的基础事件接口,利用JDK8的默认方法特性,提供了一些默认的事件处理方法。这些方法为具体的事件处理类提供了一个统一的接口规范。
- 主要方法
init()
:用于初始化事件的默认方法。在实际应用中,如果某个事件需要进行特定的初始化操作,可以重写该方法。onKeyPressed(KeyEvent event)
、onKeyReleased(KeyEvent event)
、onMouseMoved(MouseEvent event)
:分别是按键按下、释放和鼠标移动的默认事件处理方法。具体的事件处理类可以根据需要重写这些方法来实现特定的业务逻辑。
4. FFObject
类
- 类的职责
- 作为游戏中的基础对象类,继承自
FFEvent
,它定义了所有游戏对象的通用属性和行为。所有的游戏实体对象(如蛇、食物等)都继承自该类,从而实现了代码的复用和统一的行为规范。
- 作为游戏中的基础对象类,继承自
- 主要属性
- 包括位置属性(
xProperty
、yProperty
)、大小属性(widthProperty
、heightProperty
)、更新状态属性(updateProperty
)和可见性属性(visibleProperty
)等。这些属性通过JavaFX的属性绑定机制进行管理,方便在对象状态发生变化时自动更新相关的视图。
- 包括位置属性(
- 主要方法
init()
:用于初始化对象的默认方法。在该方法中,设置对象的可见性为true
。draw(GraphicsContext gc)
和update()
:这两个方法是抽象方法,需要由子类具体实现。draw
方法用于在图形上下文中绘制对象,update
方法用于更新对象的状态。- 提供了各种属性的获取和设置方法,以及移动方法(
moveX(double x)
、moveY(double y)
)和碰撞检测方法(isCollisionWith(FFObject baseObject)
)。这些方法为子类提供了基本的操作接口,用于实现对象的具体行为。
5. FFScreen
类
- 类的职责
- 作为游戏的基础展示类,继承自
Canvas
并实现FFEvent
接口。它负责管理游戏中的所有可视对象,并通过动画机制实现对象的动态更新和展示。
- 作为游戏的基础展示类,继承自
- 主要属性
List<FFObject> mObjects
:用于存储游戏中的所有可视对象。通过这个列表,可以方便地对多个对象进行统一管理,如添加、删除和遍历操作。Timeline timeline
和KeyFrame keyFrame
:这两个对象构成了JavaFX的动画机制。Timeline
定义了动画的时间轴,KeyFrame
定义了动画的关键帧。通过设置合适的时间间隔和关键帧操作,可以实现对象的动态更新。int duration
:表示动画的时间间隔,单位为毫秒。默认值为10毫秒。GameState mGameState
:用于表示游戏的当前状态,包括游戏菜单、开始、继续、暂停、帮助、设置和退出等状态。通过这个状态变量,可以方便地控制游戏的流程和行为。
- 主要方法
- 构造函数:在构造函数中,首先调用父类的构造函数创建
Canvas
对象,并设置其大小。然后初始化时间轴(initTimeLine()
),为动画的运行做好准备。 initEvents()
:用于初始化游戏中的键盘和鼠标事件。在该方法中,分别为Scene
的按键按下、释放和鼠标移动事件注册监听器,并将事件转发给相应的对象进行处理。- 针对按键按下、释放和鼠标移动的事件处理方法(
onKeyPressed(KeyEvent event)
、onKeyReleased(KeyEvent event)
、onMouseMoved(MouseEvent event)
):这些方法会遍历mObjects
列表中的所有对象,并调用对象自身的相应事件处理方法。这样可以实现对多个对象的统一事件处理。 addObject(FFObject baseObject)
、removeObject(FFObject baseObject)
和removeObjectAtIndex(int index)
:用于管理mObjects
列表中的对象。通过这些方法,可以方便地向游戏场景中添加、删除对象。draw(GraphicsContext gc)
:用于绘制游戏场景中的所有可视对象。在绘制之前,首先清除画布(gc.clearRect(0, 0, getWidth(), getHeight())
),然后遍历mObjects
列表,对于可见的对象调用其draw
方法进行绘制。update()
:用于更新游戏场景中的所有可视对象。在更新过程中,遍历mObjects
列表,对于可更新的对象(通过isUpdate()
方法判断)调用其update
方法进行更新。- 时间轴相关的方法(
initTimeLine()
、start()
、pause()
、stop()
):这些方法用于控制动画的运行状态。initTimeLine()
方法用于初始化时间轴和关键帧;start()
方法用于启动动画;pause()
方法用于暂停动画;stop()
方法用于停止动画。
- 构造函数:在构造函数中,首先调用父类的构造函数创建
(三)游戏实体类设计
1. Snake
类
- 类的职责
- 表示贪吃蛇对象,是游戏的核心实体之一。它负责维护蛇的各种属性,如位置、方向、长度、速度、生命值和得分等,并实现蛇的移动、吃食物、碰撞检测等行为逻辑。
- 主要属性
AtomicInteger score
:用于记录蛇的得分。使用AtomicInteger
类型是为了保证在多线程环境下得分的正确更新。Color snakeColor
:表示蛇的颜色。KeyCode keyCode
:用于记录蛇的当前移动方向。KeyCode tmpKeyCode
:用于记录蛇的临时移动方向。在蛇移动到下一个最小单位长度之前,新的方向会先存储在tmpKeyCode
中,待满足条件后再更新到keyCode
中。int speed
:表示蛇的移动速度,即每次移动的单位长度。int hp
:表示蛇的生命值。当蛇撞到墙壁或自身身体时,生命值会减少。int length
:表示蛇的身体长度,初始值为默认长度(DEFAULT_LENGTH
)。
- 主要方法
init()
:用于初始化蛇的所有属性。在初始化过程中,设置得分、大小、方向、颜色、速度、生命值和长度等属性的值,并将蛇的位置设置在游戏场景的左上角((0, 0)
)。draw(GraphicsContext gc)
:用于在图形上下文中绘制蛇的头部。根据蛇的颜色(snakeColor
),使用gc.fillRect()
方法绘制一个矩形表示蛇头。update()
:用于更新蛇的位置和方向。根据当前的移动方向(keyCode
),通过moveX()
和moveY()
方法移动蛇的位置。同时,在移动过程中,判断是否满足改变方向的条件(即移动到下一个最小单位长度且新方向与当前方向不冲突),如果满足条件则更新keyCode
的值。另外,还需要判断蛇是否撞到墙壁或自身身体,如果发生碰撞则调用death()
方法处理。death()
:用于处理蛇死亡的情况。当蛇死亡时,将蛇的位置重置为游戏场景的左上角((0, 0)
),方向重置为初始方向(KeyCode.RIGHT
),身体长度重置为默认长度(DEFAULT_LENGTH
),并减少生命值(reduceHp()
)。onKeyPressed(KeyEvent event)
:用于处理按键按下事件。当玩家按下方向键时,首先获取按下的键码(tmpCode
),然后判断新方向是否与当前方向冲突(即是否为反方向),如果不冲突且是有效的方向键(上、下、左、右),则将新方向存储在tmpKeyCode
中。score()
、addScore()
、getHp()
、reduceHp()
、getLength()
、addLength()
、getSnakeColor()
:这些方法分别用于获取和修改蛇的得分、生命值、长度和颜色等属性。其中,addScore()
方法通过AtomicInteger
的incrementAndGet()
方法实现得分的原子性增加。
2. SnakeBody
类
- 类的职责
- 表示贪吃蛇的身体部分,与
Snake
类紧密相关。它负责维护蛇身体的位置列表,并实现身体的绘制、更新和碰撞检测等行为逻辑。
- 表示贪吃蛇的身体部分,与
- 主要属性
Snake snake
:用于关联对应的蛇对象。通过这个关联,可以获取蛇的相关属性和方法,如蛇的位置、长度等。int length
:用于记录蛇身体的当前长度。这个长度会随着蛇吃到食物而增加。LinkedList<Point> list
:用于存储蛇身体各个部分的坐标。Point
是JavaFX中的一个类,表示二维空间中的一个点。
- 主要方法
init()
:用于初始化蛇身体的属性。在初始化过程中,首先调用父类的init()
方法,然后设置蛇身体的长度为蛇的当前长度(snake.getLength()
),并清空位置列表(list.clear()
)。接着,通过循环根据蛇的位置和长度计算出身体各个部分的初始坐标,并添加到位置列表中。draw(GraphicsContext gc)
:用于在图形上下文中绘制蛇的身体。如果蛇的身体长度小于等于1,则不进行绘制。在绘制过程中,首先获取位置列表中的第一个点(firstPoi
),判断蛇是否移动了一个身位(通过比较第一个点与蛇头的位置关系),如果移动了则进行位置更新。更新位置时,添加一个新的头部坐标到位置列表的开头(list.addFirst(poi)
),如果没有吃到食物则删除位置列表的最后一个元素(list.removeLast()
)。然后,根据位置列表中的坐标,使用gc.fillRect()
方法绘制每个身体部分(包括第一个点)。update()
:用于更新蛇身体的状态。如果蛇身体不可见,则调用init()
方法重新初始化并设置为可见。isCollisionWith(FFObject baseObject)
:用于检测蛇身体是否与其他对象发生碰撞。在检测过程中,遍历位置列表中的所有点(除了第一个点,表示蛇头),通过isCollisionWith(double x, double y, FFObject baseObject)
方法检测每个点是否与其他对象发生碰撞,如果有任何一个点发生碰撞则返回true
。
3. Food
类
- 类的职责
- 表示食物对象,是游戏中的另一个核心实体。它负责随机生成食物的位置,并实现食物的绘制和更新等行为逻辑。
- 主要属性
SecureRandom random
:用于生成随机数。通过这个随机数生成器,可以在游戏场景中随机生成食物的位置。
- 主要方法
init()
:用于初始化食物的属性。在初始化过程中,首先调用父类的init()
方法,然后设置食物的大小为最小单位长度(FFContants.MIN_X
和FFContants.MIN_Y
),并通过createRandomFood()
方法随机生成食物的位置。draw(GraphicsContext gc)
:用于在图形上下文中绘制食物。将食物绘制为一个红色的圆形(gc.fillOval()
),其位置和大小由食物的属性决定。update()
:用于更新食物的状态。如果食物不可见,则调用createRandomFood()
方法重新生成食物的位置,并设置为可见。createRandomFood()
:用于随机生成食物的位置。在生成过程中,首先通过random.nextInt()
方法分别在水平和垂直方向上生成一个随机数,这个随机数的范围是根据游戏屏幕大小和最小单位长度计算出来的,保证生成的位置是最小单位长度的整数倍。然后将生成的位置设置为食物的位置(setX(x)
和setY(y)
)。
(四)游戏界面类设计
1. GameScreen
类
- 类的职责
- 作为游戏的主界面类,负责管理游戏场景中的所有对象和游戏流程。它集成了蛇、蛇身体、食物和信息显示等各个部分,通过调用它们的方法实现游戏的运行和交互。
- 主要属性
Information info
:用于显示游戏信息的对象。通过这个对象,可以获取和设置游戏的生命值、得分等信息,并在界面上显示出来。Snake snake
、SnakeBody snakeBody
、Food food
:分别表示游戏中的蛇、蛇身体和食物对象。这些对象是游戏的核心实体,通过在GameScreen
中进行管理和协调,实现游戏的正常运行