单击游戏交流网

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 95|回复: 1

如何制作策略游戏

[复制链接]

1

主题

2

帖子

3

积分

新手上路

Rank: 1

积分
3
发表于 2022-12-17 19:20:22 | 显示全部楼层 |阅读模式
Godot游戏开发新手教程 #4
翻译自:Godot Game Development for Beginner #4
作者:Daniel Buckley
目录


  • 介绍
  • 项目文件
  • 项目设置
  • 创建Tiles
  • 创建UI
  • Tile脚本
  • 地图脚本
  • 建筑数据脚本
  • 继续Part 2
介绍

无论是回合制,还是基于资源控制的即时制,策略类游戏在全世界都是最受欢迎的游戏类型之一。这就是为什么这么多的新人和经验丰富的开发者都梦想创建一个具有平衡和挑战性游戏体验的策略游戏的原因。那么,如果我们告诉你,使用Godot完全可以制作自己的策略游戏呢?
如果这听起来令人兴奋,那么这就是为你准备的教程!通过本教程,我们将向你展示如何使用Godot引擎轻松制作一款基于资源的城市建筑游戏。你将会学习若干策略游戏的通用功能,如:

  • 制作回合制系统
  • 具有三座不同的建筑生产资源
  • 设置基于网格的放置系统
  • 使用UI显示资源,回合等
那么,如果你准备好了,坐稳,打开你的电脑,然后和Godot一起制作策略游戏吧。
开始之前,很重要的一点,本教程不会涉及Godot的基础部分。所以,如果你是一个新手,我们建议你从阅读[新手教程]开始!
如果你已经知道且想直接学习建筑布局和回合制流程,试试[Part 2]()!


项目文件

对于本项目,我们需要一些素材,如果精灵,字体。它们来自于kenney.nl和Google Fonts。当然,你也可以使用自己的资源,但在本教程里,我们将使用它们。

  • 下载精灵和字体素材以及完整的项目 点这里
项目设置

创建一个新项目,第一个scene使用2D Scene(Node2D 节点)为根节点,命名为MainScene。保存它到FileSystem。


让我们同样的将两个素材文件夹拖入FileSystem里。


我们还需要设置屏幕的分辨率。当我们按下play时(会要求你选择一个默认sence - 选择MainScene),可以看到游戏窗口很小。我们需要根据精灵的大小和tiles的数量来设置分辨率。
打开Project Settings窗口(Project > Project Settings...),来到底部的Display > Window标签。

  • 设置Width为1280
  • 设置Height为720
  • 禁用Resizble


创建Tiles

我们将从创建tile scene开始。这将包含sprite,highlight,collider,script等。那么,创建一个根节点为Area2D的新场景。在选择tiles时,Area2D可以检测碰撞。

  • 重命名为Tile
  • 保存场景


接下来,我们需要创建一些子节点。

  • Ground.png拖入场景中,创建新的Sprite节点(确保它的position为0, 0)
  • TileHightlight.png拖入场景中,创建新的Sprite节点

    • 重命名为Highlight
    • 设置Scale为6.4, 6.4
    • 点击Scene面板中,该Sprite右侧的眼睛,将它默认为隐藏

  • 创建一个新的Sprite节点

    • 重命名为BuildingIcon

  • 创建一个CollisionShape2D节点

    • 设置ShapeRectangleShape2D
    • 设置Extents为32, 32



回到MainScene,将Tile scene拖入场景中创建一个实例。为了更容易定位,选中Grid Snapping


选中Tile节点,按下Ctrl + D复制它,将20*9的区域放上Tiles。这将是我们玩游戏的地方,确保所有区域都包含了绿色方块。


你可能已经注意到,Scene面板中,层级关系相当混乱。我们100来个节点,要在里面找到其它节点,相当困难。要解决这个问题,可以这样:

  • 创建一个Node节点(大多数基础节点,我们用于容器)
  • 命名为Tiles
  • 将所有的tile节点拖到Tiles里作为子节点
  • 这样,我们可以在Scene层级中隐藏它们


这时,我们还没有为Tile写脚本,但接着我们先处理UI。
创建UI

我们的UI是在屏幕底部的长条,用于显示我们的资源,回合和按钮。创建一个新的场景,根节点类型为Control

  • 重命名为UI
  • 保存场景
  • 设置Rect > Position为0, 576
  • 设置Rect > Size为1280, 144


UI节点创建一个子节点ColorRect,命名为BG。这是一个控制节点,渲染指定颜色。

  • 设置Colordark grey
  • 设置Size为1280, 144


继续之前,我们先设置一下字段。在Font目录里,我们有2个.ttf格式的文件。为每个文件执行:

  • 右键点击.ttf文件,选择New Resource...
  • 创建一个新的DynamicFont resource


对每个dynamic font resource...

  • 双击DynamicFont resource
  • 设置Size为:

    • Regular = 30
    • Bold = 35

  • 将.ttf文件拖入Font Data属性里


接着,创建一个Button节点,命名为EndTurnButton

  • 设置TextEnd Turn
  • 设置Position为1046, 34
  • 设置Size为200, 75
  • 将bold dynamic font resource拖入Custom Fonts > Font属性里


创建Lable节点,命名为TurnText

  • 设置TextTurn 257,用于占位
  • 设置Position为878, 56
  • 设置Size为143, 36
  • 将regular dynamic font拖入Custom Fonts > Font属性里


接着,我们将创建3个建筑按钮。它们将存储在一个节点里,该节点可以自动调整其子节点的大小。创建一个HBoxContainer节点,命名为BuildingButtons

  • 设置AlignmentCenter
  • 设置Position为34, 34
  • 设置Size为236, 75


BuildingButtons创建3个子节点Button。你可以看到,它们自动的填满了BuildingButtons节点。

  • MineButton
  • GreenhouseButton
  • SolarPanelButton
为每个按钮的Icon texture设置各自的texture,它们在Sprites目录里。


创建Label节点,命名为HeaderFoodMetal

  • 设置TextFood: Metal:
  • 设置AlignRight
  • 设置Position为368,32
  • 设置Size为115,75
  • 设置Custom Font > Font属性为regular dynamic font


复制此节点,重命名为FootMetalText

  • 向下面图片一样将它移动到右侧
  • 设置Text为25(+80) 25 (+80)
  • 设置AlignLeft
  • 设置Custom Colors > Font Coloryellow/orange


选择两个labels,复制它们。重命名为:

  • HeaderOyxgenEnergy
  • OxygenEnergyText
将它们移动到右侧。修改header label文本为Oxygen: Energy:


这样子,我们的UI就算完成了。让我们回MainScene,将它拖到场景里。


现在,如果按下play,你将看到游戏最终的外观,只是没有功能。


Tile脚本

首先,我们将创建Tile脚本。它将存储每个Tile的数据和功能。在Tile场景里,选择Tile节点,为其创建一个脚本Tile。接着还是从变量开始。
# is this the starting tile?
# a Base building will be placed here at the start of the game
export var startTile = false

# do we have a building on this tile?
var hasBuilding : bool = false

# can we place a building on this tile?
var canPlace    Building : bool = false

# components
onready var highlight : Sprite = get_node("Highlight")
onready var buildingIcon : Sprite = get_node("BuildingIcon")接着,创建_ready函数(它会在节点初始化后被调用一次)。这里我们将把Tile添加到"Tiles"group里。在Godot中,group可以很容易的让我们获取或修改一组节点。
# called once when the node is initialized
func _ready ():

    # add the tile to the "Tiles" group when the node is initialized
    add_to_group("Tiles")toggle_highlight函数将处理highlight是否可见,同样也可以检测是否可以旋转建筑。
# turns on or off the green highlight
func toggle_highlight (toggle):

    highlight.visible = toggle
    canPlaceBuilding = toggleplace_building函数会在我们放置建筑到Tile上时被调用。
# called when a building is placed on the tile
# sets the tile's building texture to display it
func place_building (buildingTexture):

    hasBuilding = true
    buildingIcon.texture = buildingTexture接下来,我们需要在脚本里处理信号。input_event信号会在检测到输入时被调用一次,如鼠标点击。所以,选择Tile节点,打开Node面板,双击这个信号。


这次会在脚本里创建_on_Tile_input_event函数。我们还需要处理一些其它的事情,所以先添加pass到函数里,它的意思是空函数。
# called when an input event takes place on the tile
func _on_Tile_input_event (viewport, event, shape_idx):

    passMap脚本

Map脚本将引用我们所有的Tiles,且可以高亮可用的Tile,或获取指定位置的Tile等。在MainScene里,选择Tiles节点,为它添加Map脚本。我们仍然从变量开始。
# all the tiles in the game
var allTiles : Array

# all the tiles which have buildings on them
var tilesWithBuildings : Array

# size of a tile
var tileSize : float = 64.0get_tile_at_position函数将返回指定位置的Tile。
# returns a tile at the given position - returns null if no tile is found
func get_tile_at_position (position):

    # loop through all of the tiles
    for x in range(allTiles.size()):
        # if the tile matches our given position, return it
        if allTiles[x].position == position and allTiles[x].hasBuilding == false:
            return allTiles[x]

    return nulldisable_tile_highlights函数遍历所有的tiles,禁用它们的高亮。
# disables all of the tile highlights
func disable_tile_highlights ():

    for x in range(allTiles.size()):
        allTiles[x].toggle_highlight(false)highlight_available_tiles函数会高亮所有可以放置建筑的Tiles。在建筑周围的Tiles是可以放置建筑的。首先,我们需要遍历有建筑Tile周围的Tiles。
# highlights the tiles we can place buildings on
func highlight_available_tiles ():

    # loop through all of the tiles with buildings
    for x in range(tilesWithBuildings.size()):在循环体里,我们首先需要获取当前位置相邻北,南,东和西位置的Tile。
# get the tile north, south, east and west of this one
var northTile = get_tile_at_position(tilesWithBuildings[x].position + Vector2(0, tileSize))
var southTile = get_tile_at_position(tilesWithBuildings[x].position + Vector2(0, -tileSize))
var eastTile = get_tile_at_position(tilesWithBuildings[x].position + Vector2(tileSize, 0))
var westTile = get_tile_at_position(tilesWithBuildings[x].position + Vector2(-tileSize, 0))之后,检查每一个Tile,如果它们不为null,高亮它。
# if the directional tiles aren't null, toggle their highlight - allowing us to build
if northTile != null:
    northTile.toggle_highlight(true)
if southTile != null:
    southTile.toggle_highlight(true)
if eastTile != null:
    eastTile.toggle_highlight(true)
if westTile != null:
    westTile.toggle_highlight(true)这时,我们还没有完成Map脚本,但我们需要先处理一些其它事情。
BuildingData脚本

BuildingData脚本不是一个节点脚本。它是一个全局的脚本,可以在任何地方调用。在Script标签里,执行File > New Script...创建名为BuildingDatta的脚本。
接着,需要打开Project Settiings(Project > Project Settings...),点击AutoLoad标签。

  • 选择文件按钮,找到BuildingData.gd脚本
  • 点击Add按钮
这会将BuildingData脚本创建为单例。


回到BuildingData脚本,创建一个新class,Building。它是保存每个建筑数据的对象。
class Building:

    # building type
    var type : int

    # building texture
    var iconTexture : Texture

    # resource the building produces
    var prodResource : int = 0
    var prodResourceAmount : int

    # resource the building needs to be maintained
    var upkeepResource : int = 0
    var upkeepResourceAmount : int

    func _init (type, iconTexture, prodResource, prodResourceAmount, upkeepResource, upkeepResourceAmount):
        self.type = type
        self.iconTexture = iconTexture
        self.prodResource = prodResource
        self.prodResourceAmount = prodResourceAmount
        self.upkeepResource = upkeepResource
        self.upkeepResourceAmount = upkeepResourceAmount建筑类型和资源,使用integer类型。这些整数代表不同的对象。
Building Types

  • Base = 0
  • Mine = 1
  • Greenhouse = 2
  • Solar Panel = 3
Resources

  • None = 0
  • Food = 1
  • Metal = 2
  • Oxygen = 3
  • Energy = 4
在class外部,为4个建筑分别创建对应的变量(Base, Mine, Greenhouse, Solar Panel)。
var base = Building.new(0, preload("res://Sprites/Base.png"), 0, 0, 0, 0)
var mine = Building.new(1, preload("res://Sprites/Mine.png"), 2, 1, 4, 1)
var greenhouse = Building.new(2, preload("res://Sprites/Greenhouse.png"), 1, 1, 0, 0)
var solarpanel = Building.new(3, preload("res://Sprites/SolarPanel.png"), 4, 1, 0, 0)通过这些变量可以知道每个建筑生产,获取,贴图或其它值。
继续Part 2

这里确实有太多的元素需要添加,我们先就到这里。做为游戏基础,我们已经创建了基于网格的Tile系统,它允许我们放置建筑,并高亮可以放置建筑的位置。另外,我们还设置了建筑,它们将为我们的游戏提供非常关键的资源管理功能(以及相关的UI)。
尽管这是使用Godot构建策略游戏良好的开端,但我们还有许多工作要做!在Part 2,我们将继续完成地图,创建放置建筑的功能,创建回合制的流程,并将这些功能联系起来,让它变得完整!
回复

使用道具 举报

1

主题

3

帖子

5

积分

新手上路

Rank: 1

积分
5
发表于 2022-12-17 19:20:56 | 显示全部楼层
正好毕业设计打算用godot做一个历史策略游戏,来学习一下[爱]
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|单击游戏交流网

Copyright © 2001-2013 Comsenz Inc.Template by Comsenz Inc.All Rights Reserved.

Powered by Discuz!X3.4

快速回复 返回顶部 返回列表