8 Commits

Author SHA1 Message Date
e22b888bfc doc: fix bme adder doc 2025-09-01 13:16:02 +08:00
88ef1d3202 doc: fix legacy align doc 2025-09-01 13:05:51 +08:00
f2af90c876 doc: fix export target for virtools exporting in doc 2025-09-01 12:56:19 +08:00
4dba3c3a71 doc: add game camera doc 2025-09-01 11:00:34 +08:00
e31a677d83 doc: add version rules in doc 2025-09-01 10:12:44 +08:00
35fcbe54b5 fix: re-design the layout of game camera.
- use more friendly layout in game camera. reported by zzq.
2025-08-30 22:50:43 +08:00
9e83fe0a10 i18n: update i18n
- update i18n template and translation.
- fix lost translation context in code.
2025-08-26 21:54:32 +08:00
33fb1a65d3 chore: remove useless gitkeep 2025-08-26 20:47:06 +08:00
17 changed files with 2041 additions and 1578 deletions

View File

@ -50,7 +50,8 @@ class BBP_OT_game_resolution(bpy.types.Operator):
name = "Resolution Kind", name = "Resolution Kind",
description = "The type of preset resolution.", description = "The type of preset resolution.",
items = _g_EnumHelper_ResolutionKind.generate_items(), items = _g_EnumHelper_ResolutionKind.generate_items(),
default = _g_EnumHelper_ResolutionKind.to_selection(ResolutionKind.Normal) default = _g_EnumHelper_ResolutionKind.to_selection(ResolutionKind.Normal),
translation_context = 'BBP_OT_game_resolution/property'
) # type: ignore ) # type: ignore
def invoke(self, context, event): def invoke(self, context, event):
@ -185,37 +186,96 @@ class BBP_OT_game_camera(bpy.types.Operator):
name = "Target Kind", name = "Target Kind",
description = "", description = "",
items = _g_EnumHelper_TargetKind.generate_items(), items = _g_EnumHelper_TargetKind.generate_items(),
default = _g_EnumHelper_TargetKind.to_selection(TargetKind.Cursor) default = _g_EnumHelper_TargetKind.to_selection(TargetKind.Cursor),
translation_context = 'BBP_OT_game_camera/property'
) # type: ignore ) # type: ignore
rotation_kind: bpy.props.EnumProperty( rotation_kind: bpy.props.EnumProperty(
name = "Rotation Angle Kind", name = "Rotation Angle Kind",
description = "", description = "",
items = _g_EnumHelper_RotationKind.generate_items(), items = _g_EnumHelper_RotationKind.generate_items(),
default = _g_EnumHelper_RotationKind.to_selection(RotationKind.Preset) default = _g_EnumHelper_RotationKind.to_selection(RotationKind.Preset),
translation_context = 'BBP_OT_game_camera/property'
) # type: ignore ) # type: ignore
preset_rotation_angle: bpy.props.EnumProperty( preset_rotation_angle: bpy.props.EnumProperty(
name = "Preset Rotation Angle", # I18N: Property not showen should not have name and desc.
description = "", # name = "Preset Rotation Angle",
# description = "",
options = {'HIDDEN'},
items = _g_EnumHelper_RotationAngle.generate_items(), items = _g_EnumHelper_RotationAngle.generate_items(),
default = _g_EnumHelper_RotationAngle.to_selection(RotationAngle.Deg0) default = _g_EnumHelper_RotationAngle.to_selection(RotationAngle.Deg0),
) # type: ignore
def preset_rotation_angle_deg_getter(self, probe) -> bool:
return _g_EnumHelper_RotationAngle.get_selection(self.preset_rotation_angle) == probe
def preset_rotation_angle_deg_setter(self, val) -> None:
self.preset_rotation_angle = _g_EnumHelper_RotationAngle.to_selection(val)
return None
preset_rotation_angle_deg0: bpy.props.BoolProperty(
name = "0 Degree",
translation_context = 'BBP_OT_game_camera/property',
get = lambda self: BBP_OT_game_camera.preset_rotation_angle_deg_getter(self, RotationAngle.Deg0),
set = lambda self, _: BBP_OT_game_camera.preset_rotation_angle_deg_setter(self, RotationAngle.Deg0)
) # type: ignore
preset_rotation_angle_deg45: bpy.props.BoolProperty(
name = "45 Degree",
translation_context = 'BBP_OT_game_camera/property',
get = lambda self: BBP_OT_game_camera.preset_rotation_angle_deg_getter(self, RotationAngle.Deg45),
set = lambda self, _: BBP_OT_game_camera.preset_rotation_angle_deg_setter(self, RotationAngle.Deg45)
) # type: ignore
preset_rotation_angle_deg90: bpy.props.BoolProperty(
name = "90 Degree",
translation_context = 'BBP_OT_game_camera/property',
get = lambda self: BBP_OT_game_camera.preset_rotation_angle_deg_getter(self, RotationAngle.Deg90),
set = lambda self, _: BBP_OT_game_camera.preset_rotation_angle_deg_setter(self, RotationAngle.Deg90)
) # type: ignore
preset_rotation_angle_deg135: bpy.props.BoolProperty(
name = "135 Degree",
translation_context = 'BBP_OT_game_camera/property',
get = lambda self: BBP_OT_game_camera.preset_rotation_angle_deg_getter(self, RotationAngle.Deg135),
set = lambda self, _: BBP_OT_game_camera.preset_rotation_angle_deg_setter(self, RotationAngle.Deg135)
) # type: ignore
preset_rotation_angle_deg180: bpy.props.BoolProperty(
name = "180 Degree",
translation_context = 'BBP_OT_game_camera/property',
get = lambda self: BBP_OT_game_camera.preset_rotation_angle_deg_getter(self, RotationAngle.Deg180),
set = lambda self, _: BBP_OT_game_camera.preset_rotation_angle_deg_setter(self, RotationAngle.Deg180)
) # type: ignore
preset_rotation_angle_deg225: bpy.props.BoolProperty(
name = "225 Degree",
translation_context = 'BBP_OT_game_camera/property',
get = lambda self: BBP_OT_game_camera.preset_rotation_angle_deg_getter(self, RotationAngle.Deg225),
set = lambda self, _: BBP_OT_game_camera.preset_rotation_angle_deg_setter(self, RotationAngle.Deg225)
) # type: ignore
preset_rotation_angle_deg270: bpy.props.BoolProperty(
name = "270 Degree",
translation_context = 'BBP_OT_game_camera/property',
get = lambda self: BBP_OT_game_camera.preset_rotation_angle_deg_getter(self, RotationAngle.Deg270),
set = lambda self, _: BBP_OT_game_camera.preset_rotation_angle_deg_setter(self, RotationAngle.Deg270)
) # type: ignore
preset_rotation_angle_deg315: bpy.props.BoolProperty(
name = "315 Degree",
translation_context = 'BBP_OT_game_camera/property',
get = lambda self: BBP_OT_game_camera.preset_rotation_angle_deg_getter(self, RotationAngle.Deg315),
set = lambda self, _: BBP_OT_game_camera.preset_rotation_angle_deg_setter(self, RotationAngle.Deg315)
) # type: ignore ) # type: ignore
custom_rotation_angle: bpy.props.FloatProperty( custom_rotation_angle: bpy.props.FloatProperty(
name = "Custom Rotation Angle", name = "Custom Rotation Angle",
description = "The rotation angle of camera relative to 3D Cursor", description = "The rotation angle of camera relative to 3D Cursor or Active Object",
subtype = 'ANGLE', subtype = 'ANGLE',
min = 0, max = math.radians(360), min = 0, max = math.radians(360),
step = 100, step = 100,
# MARK: What the fuck of the precision? # MARK: What the fuck of the precision?
# I set it to 2 but it doesn't work so I forcely set it to 100. # I set it to 2 but it doesn't work so I forcely set it to 100.
precision = 100, precision = 100,
translation_context = 'BBP_OT_game_camera/property'
) # type: ignore ) # type: ignore
perspective_kind: bpy.props.EnumProperty( perspective_kind: bpy.props.EnumProperty(
name = "Rotation Angle Kind", name = "Rotation Angle Kind",
description = "", description = "",
items = _g_EnumHelper_PerspectiveKind.generate_items(), items = _g_EnumHelper_PerspectiveKind.generate_items(),
default = _g_EnumHelper_PerspectiveKind.to_selection(PerspectiveKind.Ordinary) default = _g_EnumHelper_PerspectiveKind.to_selection(PerspectiveKind.Ordinary),
translation_context = 'BBP_OT_game_camera/property'
) # type: ignore ) # type: ignore
@classmethod @classmethod
@ -249,7 +309,19 @@ class BBP_OT_game_camera(bpy.types.Operator):
rot_kind = _g_EnumHelper_RotationKind.get_selection(self.rotation_kind) rot_kind = _g_EnumHelper_RotationKind.get_selection(self.rotation_kind)
match rot_kind: match rot_kind:
case RotationKind.Preset: case RotationKind.Preset:
layout.prop(self, 'preset_rotation_angle', text='') # for preset angles, we show a special layout (grid view)
subgrid = layout.grid_flow(row_major=True, columns=3, even_columns=True, even_rows=True, align=True)
subgrid.prop(self, 'preset_rotation_angle_deg315', toggle = 1)
subgrid.prop(self, 'preset_rotation_angle_deg0', toggle = 1)
subgrid.prop(self, 'preset_rotation_angle_deg45', toggle = 1)
subgrid.prop(self, 'preset_rotation_angle_deg270', toggle = 1)
subicon = subgrid.row()
subicon.alignment = 'CENTER'
subicon.label(text='', icon='MESH_CIRCLE') # show a 3d circle as icon
subgrid.prop(self, 'preset_rotation_angle_deg90', toggle = 1)
subgrid.prop(self, 'preset_rotation_angle_deg225', toggle = 1)
subgrid.prop(self, 'preset_rotation_angle_deg180', toggle = 1)
subgrid.prop(self, 'preset_rotation_angle_deg135', toggle = 1)
case RotationKind.Custom: case RotationKind.Custom:
layout.prop(self, 'custom_rotation_angle', text='') layout.prop(self, 'custom_rotation_angle', text='')

View File

@ -137,7 +137,7 @@ class BBP_OT_legacy_align(bpy.types.Operator):
return None return None
apply_flag: bpy.props.BoolProperty( apply_flag: bpy.props.BoolProperty(
# TR: Property not showen should not have name and desc. # I18N: Property not showen should not have name and desc.
# name = "Apply Flag", # name = "Apply Flag",
# description = "Internal flag.", # description = "Internal flag.",
options = {'HIDDEN', 'SKIP_SAVE'}, options = {'HIDDEN', 'SKIP_SAVE'},
@ -145,14 +145,14 @@ class BBP_OT_legacy_align(bpy.types.Operator):
update = apply_flag_updated update = apply_flag_updated
) # type: ignore ) # type: ignore
recursive_hinder: bpy.props.BoolProperty( recursive_hinder: bpy.props.BoolProperty(
# TR: Property not showen should not have name and desc. # I18N: Property not showen should not have name and desc.
# name = "Recursive Hinder", # name = "Recursive Hinder",
# description = "An internal flag to prevent the loop calling to apply_flags's updator.", # description = "An internal flag to prevent the loop calling to apply_flags's updator.",
options = {'HIDDEN', 'SKIP_SAVE'}, options = {'HIDDEN', 'SKIP_SAVE'},
default = False default = False
) # type: ignore ) # type: ignore
align_history : bpy.props.CollectionProperty( align_history : bpy.props.CollectionProperty(
# TR: Property not showen should not have name and desc. # I18N: Property not showen should not have name and desc.
# name = "Historys", # name = "Historys",
# description = "Align history.", # description = "Align history.",
type = BBP_PG_legacy_align_history type = BBP_PG_legacy_align_history

View File

@ -43,7 +43,7 @@ class BBP_PG_ptrprop_resolver(bpy.types.PropertyGroup):
translation_context = 'BBP_PG_ptrprop_resolver/property' translation_context = 'BBP_PG_ptrprop_resolver/property'
) # type: ignore ) # type: ignore
# TR: Properties not showen should not have name and desc. # I18N: Properties not showen should not have name and desc.
ioport_encodings: bpy.props.CollectionProperty(type = BBP_PG_bmap_encoding) # type: ignore ioport_encodings: bpy.props.CollectionProperty(type = BBP_PG_bmap_encoding) # type: ignore
active_ioport_encodings: bpy.props.IntProperty() # type: ignore active_ioport_encodings: bpy.props.IntProperty() # type: ignore

View File

@ -954,6 +954,7 @@ class BBP_OT_preset_virtools_material(bpy.types.Operator):
name = "Preset", name = "Preset",
description = "The preset which you want to apply.", description = "The preset which you want to apply.",
items = _g_Helper_MtlPreset.generate_items(), items = _g_Helper_MtlPreset.generate_items(),
translation_context = 'BBP_OT_preset_virtools_material/property'
) # type: ignore ) # type: ignore
@classmethod @classmethod

View File

@ -48,7 +48,7 @@ import bpy
# - If we use `bpy.app.translations.pgettext` with other non-Blender functions, such as `print`. # - If we use `bpy.app.translations.pgettext` with other non-Blender functions, such as `print`.
# * Use it as a normal function. # * Use it as a normal function.
# #
# All translation annotation are started with `TR:` # All translation annotation are started with `I18N:`
# #
# The universal translation context prefix for BBP_NG plugin. # The universal translation context prefix for BBP_NG plugin.

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 80 KiB

View File

@ -1,15 +1,25 @@
# 添加路面 # 添加路面
!!! info "BME是可扩展的"
BME的路面添加器是可扩展的菜单中的每一个项实际上都由一组JSON数据描述。您可以阅读[技术信息](./tech-infos.md)章节来了解我们是如何编写这些JSON的甚至您还可以根据你的需求自行扩展BME可创建的路面种类。
## 开始生成 ## 开始生成
### 从添加菜单生成
在3D视图中点击`Add - Floors`可展开添加路面菜单。菜单如下图所示。 在3D视图中点击`Add - Floors`可展开添加路面菜单。菜单如下图所示。
![](../imgs/bme-adder.png) ![](../imgs/bme-adder.png)
点击菜单后可以在弹出的子菜单中查看所有受支持的路面类型。其名称和图标提示了它所要创建路面的样式与形状。 点击菜单后可以在弹出的子菜单中按分类查看所有受支持的路面类型。其名称和图标提示了它所要创建路面的样式与形状。
!!! info "BME是可扩展的" ### 从侧边栏生成
BME的路面添加器是可扩展的菜单中的每一个项实际上都由一组JSON数据描述。您可以阅读[技术信息](./tech-infos.md)章节来了解我们是如何编写这些JSON的甚至您还可以根据你的需求自行扩展BME可创建的路面种类。
此外还可以通过点按N键打开3D视图的侧边栏在其中找到Ballance选项卡展开Floor面板也可找到如下图所示
![](../imgs/bme-adder-sidebar.png)
该面板相较于添加菜单,其好处在于可常驻在界面之中,避免了在持续添加路面的操作中,频繁打开菜单寻找路面的麻烦。此外,该选项卡中还有用于添加钢轨和机关的面板可供展开,在后续章节中不再赘述。
## 配置路面 ## 配置路面

View File

@ -39,7 +39,12 @@ Conflict Options冲突解决选项章节指示了当导入器遇到物体
### 导出目标 ### 导出目标
Export Target导出目标章节用于决定你需要将哪写物体导出到Virtools文档中。你可以选择导出一个集合或一个物体,并在下面选择对应的集合或物体。需要注意的是,选择集合的时候,会将内部集合中的物体也一起导出,即支持嵌套集合的导出。 Export Target导出目标章节用于决定你需要将哪写物体导出到Virtools文档中。你可以在四种模式中选择其一,来决定需要导出的内容:
* Object导出单个物体。选择后需要在下面选择需要导出的物体。
* Collection导出单个集合**这是最常用的选项**。选择后需要在下面选择需要导出的集合。值得注意的是,该选项支持嵌套集合导出,即选择集合的时候,会将集合中的集合的物体也一起导出。
* Selected Objects导出选择的物体。你需要在导出前选择好需要导出的物体。
* All Objects导出该文档中的所有物体。该选项慎用因为它是粗暴地遍历文档中的物体列表来进行导出很可能会导出许多你不需要的物体。
### Virtools参数 ### Virtools参数

View File

@ -14,9 +14,11 @@
在面板中,`Align Axis`指定了你要对齐的轴,此处可以多选以指定多个轴,不指定任何轴将无法进行对齐操作,因而也无法点击`Apply`按钮。 在面板中,`Align Axis`指定了你要对齐的轴,此处可以多选以指定多个轴,不指定任何轴将无法进行对齐操作,因而也无法点击`Apply`按钮。
`Current Object`是对齐参考物体,也就是场景中的活动物体,通常也就是你选择的最后一个物体。在这个选项里指定你需要参考其什么数值进行对齐,分别有`Min`(轴上最小值)、`Center (Bounding Box)`(碰撞箱的中心)、`Center (Axis)`(物体的原点)、`Max`轴上的最大值可选。这些选项与3ds Max中的对齐选项是一致的 `Current Object`指示选择哪个实例作为对齐参考。你可以选择场景中的活动物体,通常也就是你选择的最后一个物体。或者是3D游标。需要注意的是如果你选择活动物体模式那么活动物体将排除在对齐操作之外不会被移动因为参考物体是不可动的。而如果你选择3D游标则活动物体会被纳入对齐操作的范围之中
`Target Objects`是正在被对齐的物体,可能有很多个,在这个选项里也是指定你需要参考其什么数值进行对齐。选项与`Current Object`含义一致。 `Current Object Align Mode`是对齐参考物体的对齐模式它只有在你选择活动物体作为对齐参考物体时才会出现。因为3D游标是一个单纯的点而物体占有一定体积我们需要按照某种模式后文叙述在空间中选择一个点作为后续对齐操作时使用的点。在这个选项里指定你需要参考其什么数值进行对齐,分别有`Min`(轴上最小值)、`Center (Bounding Box)`(碰撞箱的中心)、`Center (Axis)`(物体的原点)、`Max`轴上的最大值可选。这些选项与3ds Max中的对齐选项是一致
`Target Objects Align Mode`是正在被对齐的物体,可能有很多个,在这个选项里也是指定你需要参考其什么数值进行对齐。选项与`Current Object Align Mode`含义一致。
`Apply`按钮点击后将把当前页面的配置压入操作栈,并重置上面的设置,使得你可以开始新一轮对齐操作而无需再次执行传统对齐。操作栈中的操作个数在`Apply`按钮下方显示。 `Apply`按钮点击后将把当前页面的配置压入操作栈,并重置上面的设置,使得你可以开始新一轮对齐操作而无需再次执行传统对齐。操作栈中的操作个数在`Apply`按钮下方显示。

View File

@ -1,9 +1,13 @@
# 技术信息 # 技术信息
## 标准与协议文档
* BM文件标准https://github.com/yyc12345/gist/blob/master/BMFileSpec/BMSpec_ZH.md * BM文件标准https://github.com/yyc12345/gist/blob/master/BMFileSpec/BMSpec_ZH.md
* 制图工具链标准及`meshes`文件夹下的文件的格式https://github.com/yyc12345/gist/blob/master/BMFileSpec/YYCToolsChainSpec_ZH.md * 制图工具链标准及`meshes`文件夹下的文件的格式https://github.com/yyc12345/gist/blob/master/BMFileSpec/YYCToolsChainSpec_ZH.md
* BMERevenge的JSON文件的格式https://github.com/yyc12345/gist/blob/master/BMERevenge/DevDocument_v2.0_ZH.md * BMERevenge的JSON文件的格式https://github.com/yyc12345/gist/blob/master/BMERevenge/DevDocument_v2.0_ZH.md
## 开发辅助包
本插件配合了`fake-bpy-module`模块来实现类型提示以加快开发速度。使用如下命令来安装Blender的类型提示库。 本插件配合了`fake-bpy-module`模块来实现类型提示以加快开发速度。使用如下命令来安装Blender的类型提示库。
* Blender 3.6: `pip install fake-bpy-module-latest==20230627` * Blender 3.6: `pip install fake-bpy-module-latest==20230627`
@ -11,3 +15,16 @@
* Blender 4.5: `pip install fake-bpy-module-latest==20250604` * Blender 4.5: `pip install fake-bpy-module-latest==20250604`
这么做主要是因为`fake-bpy-module`没有很及时地发布适用于指定Blender版本的包因此我只能通过选择最接近Blender对应版本离开`main`主线时间的每日编译版本来安装它(因为每日编译版本只编译`main`主线)。 这么做主要是因为`fake-bpy-module`没有很及时地发布适用于指定Blender版本的包因此我只能通过选择最接近Blender对应版本离开`main`主线时间的每日编译版本来安装它(因为每日编译版本只编译`main`主线)。
!!! question "为什么不采用Blender官方的bpy模块"
Blender在PyPI上提供了官方的名为`bpy`的包但我们不会采用它作为我们的开发辅助包。因为它基本上就是将Blender打包成了一个模块也就意味着你基本上又把Blender重新下载了一遍使得你可以通过Python来操纵Blender。这与我们使用一个仅提供类型提示的包来辅助插件开发的目的相悖。
## 版本号规则
BBP的版本号格式遵循[语义化版本](https://semver.org/lang/zh-CN/)。但略有区别:
* 主版本号只在重构整个插件时提升。
* 次版本号是常规更新使用。
* 修订号则是在不修改任何功能的情况下递增的版本号。例如4.2.1版本仅增加了对macOS Blender的更新不更改任何功能。
在BBP发布一个正式版前通常有3个阶段性版本分别是Alpha版本Beta版本和RC版本。Alpha版本专注于功能性更新用于检验新添加或修改的功能是否正常工作不包含文档和翻译。Beta版本则专注于插件文档而RC版本则关注于插件翻译。但这三个版本并非总是存在如果更新内容较少则可能会跳过其中一些版本或直接进行发布。

View File

@ -7,3 +7,31 @@
窥视归组并转换为网格其全称为窥视并复制曲线倒角物体的Virtools归组信息后再转换为网格。你可以选中一些物体后右键在物体上下文菜单中找到这一功能。 窥视归组并转换为网格其全称为窥视并复制曲线倒角物体的Virtools归组信息后再转换为网格。你可以选中一些物体后右键在物体上下文菜单中找到这一功能。
该功能正如其名,其将选中的物体转换为网格,如果选中的物体是曲线,且设置了倒角物体,则将倒角物体的归组信息赋予当前曲线(覆盖曲线当前归组设置)。如果选中的物体不是曲线,或者是曲线但没有倒角物体,那么该功能与执行转换为网格无异。该功能在放样建模时极为有用,因为只需要为截面物体进行正确的归组,然后再使用此功能将曲线转换为网格,就可以确保放样后的物体归组正确。 该功能正如其名,其将选中的物体转换为网格,如果选中的物体是曲线,且设置了倒角物体,则将倒角物体的归组信息赋予当前曲线(覆盖曲线当前归组设置)。如果选中的物体不是曲线,或者是曲线但没有倒角物体,那么该功能与执行转换为网格无异。该功能在放样建模时极为有用,因为只需要为截面物体进行正确的归组,然后再使用此功能将曲线转换为网格,就可以确保放样后的物体归组正确。
## 游戏内摄像机
许多制图者在制作地图时,往往对地图大小没有概念,很容易创建出过大或过小的地图。菜单项`Ballance - Game Camera`的游戏内摄像机功能则提供了一种在Blender内以游戏内摄像机视角预览地图的功能方便制图者可以把握地图的大小。
为了使用该功能,你的场景中必须首先有一个摄像机,且被设置为场景的活动摄像机(无摄像机时,通过添加菜单新添加的摄像机会被自动设置)。然后你还需要一个活动物体,且该活动物体不能是这个摄像机。
!!! question "为什么一定需要活动物体?"
由于Blender插件的限制活动物体是必须的因为游戏内摄像机功能支持以3D游标 **或活动物体** 为目标。
通常只有在一个空白的Blender文档中才会出现无活动物体可用的情况进而导致该功能不可用。对于一张自制地图最不缺少的就是可成为活动物体的物体只需要随便点击一个物体就可以得到当想使用3D游标作为目标时
点击该功能后,视图将自动转为活动摄像机的视角,方便你进行预览。你可以在左下角的面板中调整相关设置。
![](../imgs/game-camera.png)
首先是选择目标实际上就是选择玩家球位于哪里。如果选择3D游标则将3D游标视为玩家球。如果选择活动物体则将玩家球放置在活动物体的原点。
!!! info "玩家球的位置并非那么简单"
无论是选择3D游标还是选择活动物体如果不做特别精细的调整其预览的结果和游戏中会有细微出入。
当你使用3D游标模式并将其简单地吸附到地面上时它并非代表球的位置。因为玩家球是一个半径为2的球实际上你需要向+Z方向移动2个单位长度才能够得到和游戏中一模一样的视角。但这个操作过于繁琐不执行这个操作预览的效果也不会偏差太多。
当你使用活动物体模式时需要注意物体的原点是选择物体时显示的那个圆点的位置。对于大多数物体这可能并非你想要的因为他们可能位于物体内部或外部并非总位于物体表面或者某个面的中心。针对这种情况我建议你改用3D游标作为目标并通过进入编辑模式灵活运用吸附和`Shift + S`菜单,来将游标放置在正确的位置。
然后是选择摄像机的旋转角度。我们提供了8种游戏内预设角度分别对应90度和45度的各4种。此外如果这些预设角度不能满足你的需求你还可以设置自定义角度。
最后是选择摄像机视角分为Ordinary常规视角Lift按住空格键的视角和Easter Egg彩蛋视角三种。

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File