editor_entity_utils.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. """
  2. All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
  3. its licensors.
  4. For complete copyright and license terms please see the LICENSE at the root of this
  5. distribution (the "License"). All use of this software is governed by the License,
  6. or, if provided, by the license below or the license accompanying this file. Do not
  7. remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. """
  10. # Built-in Imports
  11. from __future__ import annotations
  12. from typing import List, Tuple, Union
  13. # Helper file Imports
  14. import utils
  15. # Lumberyard Imports
  16. import azlmbr
  17. import azlmbr.bus as bus
  18. import azlmbr.editor as editor
  19. import azlmbr.math as math
  20. import azlmbr.legacy.general as general
  21. class EditorComponent:
  22. """
  23. EditorComponent class used to set and get the component property value using path
  24. EditorComponent object is returned from either of
  25. EditorEntity.add_component() or Entity.add_components() or EditorEntity.get_component_objects()
  26. which also assigns self.id and self.type_id to the EditorComponent object.
  27. """
  28. # Methods
  29. def get_component_name(self) -> str:
  30. """
  31. Used to get name of component
  32. :return: name of component
  33. """
  34. type_names = editor.EditorComponentAPIBus(bus.Broadcast, "FindComponentTypeNames", [self.type_id])
  35. assert len(type_names) != 0, "Component object does not have type id"
  36. return type_names[0]
  37. def get_property_tree(self):
  38. """
  39. Used to get the property tree object of component that has following functions associated with it:
  40. 1. prop_tree.is_container(path)
  41. 2. prop_tree.get_container_count(path)
  42. 3. prop_tree.reset_container(path)
  43. 4. prop_tree.add_container_item(path, key, item)
  44. 5. prop_tree.remove_container_item(path, key)
  45. 6. prop_tree.update_container_item(path, key, value)
  46. 7. prop_tree.get_container_item(path, key)
  47. :return: Property tree object of a component
  48. """
  49. build_prop_tree_outcome = editor.EditorComponentAPIBus(
  50. bus.Broadcast, "BuildComponentPropertyTreeEditor", self.id
  51. )
  52. assert (
  53. build_prop_tree_outcome.IsSuccess()
  54. ), f"Failure: Could not build property tree of component: '{self.get_component_name()}'"
  55. prop_tree = build_prop_tree_outcome.GetValue()
  56. utils.Report.info(prop_tree.build_paths_list())
  57. return prop_tree
  58. def get_component_property_value(self, component_property_path: str):
  59. """
  60. Given a component property path, outputs the property's value
  61. :param component_property_path: String of component property. (e.g. 'Settings|Visible')
  62. :return: Value set in given component_property_path. Type is dependent on component property
  63. """
  64. get_component_property_outcome = editor.EditorComponentAPIBus(
  65. bus.Broadcast, "GetComponentProperty", self.id, component_property_path
  66. )
  67. assert (
  68. get_component_property_outcome.IsSuccess()
  69. ), f"Failure: Could not get value from {self.get_component_name()} : {component_property_path}"
  70. return get_component_property_outcome.GetValue()
  71. def set_component_property_value(self, component_property_path: str, value: object):
  72. """
  73. Used to set component property value
  74. :param component_property_path: Path of property in the component to act on
  75. :param value: new value for the variable being changed in the component
  76. """
  77. outcome = editor.EditorComponentAPIBus(
  78. bus.Broadcast, "SetComponentProperty", self.id, component_property_path, value
  79. )
  80. assert (
  81. outcome.IsSuccess()
  82. ), f"Failure: Could not set value to '{self.get_component_name()}' : '{component_property_path}'"
  83. @staticmethod
  84. def get_type_ids(component_names: list) -> list:
  85. """
  86. Used to get type ids of given components list
  87. :param: component_names: List of components to get type ids
  88. :return: List of type ids of given components.
  89. """
  90. type_ids = editor.EditorComponentAPIBus(
  91. bus.Broadcast, "FindComponentTypeIdsByEntityType", component_names, azlmbr.entity.EntityType().Game
  92. )
  93. return type_ids
  94. class EditorEntity:
  95. """
  96. Entity class is used to create and interact with Editor Entities.
  97. Example: To create Editor Entity, Use the code:
  98. test_entity = Entity.create_editor_entity("TestEntity")
  99. # This creates a python object with 'test_entity' linked to entity name "TestEntity" in Editor.
  100. # To add component, use:
  101. test_entity.add_component(<COMPONENT_NAME>)
  102. """
  103. def __init__(self, id: azlmbr.entity.EntityId):
  104. self.id: azlmbr.entity.EntityId = id
  105. # Creation functions
  106. @classmethod
  107. def find_editor_entity(cls, entity_name: str) -> EditorEntity:
  108. """
  109. Given Entity name, outputs entity object
  110. :param entity_name: Name of entity to find
  111. :return: EditorEntity class object
  112. """
  113. entity_id = general.find_editor_entity(entity_name)
  114. assert entity_id.IsValid(), f"Failure: Couldn't find entity with name: '{entity_name}'"
  115. entity = cls(entity_id)
  116. return entity
  117. @classmethod
  118. def create_editor_entity(cls, name: str = None, parent_id=None) -> EditorEntity:
  119. """
  120. Used to create entity at default position using 'CreateNewEntity' Bus
  121. :param name: Name of the Entity to be created
  122. :param parent_id: (optional) Used to create child entity under parent_id if specified
  123. :return: EditorEntity class object
  124. """
  125. if parent_id is None:
  126. parent_id = azlmbr.entity.EntityId()
  127. new_id = azlmbr.editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", parent_id)
  128. assert new_id.IsValid(), "Failure: Could not create Editor Entity"
  129. entity = cls(new_id)
  130. if name:
  131. entity.set_name(name)
  132. return entity
  133. @classmethod
  134. def create_editor_entity_at(
  135. cls,
  136. entity_position: Union[List, Tuple, math.Vector3],
  137. name: str = None,
  138. parent_id: azlmbr.entity.EntityId = None,
  139. ) -> EditorEntity:
  140. """
  141. Used to create entity at position using 'CreateNewEntityAtPosition' Bus.
  142. :param entity_position: World Position(X, Y, Z) of entity in viewport.
  143. Example: [512.0, 512.0, 32.0]
  144. :param name: Name of the Entity to be created
  145. :parent_id: (optional) Used to create child entity under parent_id if specified
  146. :Example: test_entity = EditorEntity.create_editor_entity_at([512.0, 512.0, 32.0], "TestEntity")
  147. :return: EditorEntity class object
  148. """
  149. def convert_to_azvector3(xyz) -> math.Vector3:
  150. if isinstance(xyz, Tuple) or isinstance(xyz, List):
  151. assert len(xyz) == 3, ValueError("vector must be a 3 element list/tuple or azlmbr.math.Vector3")
  152. return math.Vector3(*xyz)
  153. elif isinstance(xyz, type(math.Vector3())):
  154. return xyz
  155. else:
  156. raise ValueError("vector must be a 3 element list/tuple or azlmbr.math.Vector3")
  157. if parent_id is None:
  158. parent_id = azlmbr.entity.EntityId()
  159. new_id = azlmbr.editor.ToolsApplicationRequestBus(
  160. bus.Broadcast, "CreateNewEntityAtPosition", convert_to_azvector3(entity_position), parent_id
  161. )
  162. assert new_id.IsValid(), "Failure: Could not create Editor Entity"
  163. entity = cls(new_id)
  164. if name:
  165. entity.set_name(name)
  166. return entity
  167. # Methods
  168. def set_name(self, entity_name: str):
  169. """
  170. Given entity_name, sets name to Entity
  171. :param: entity_name: Name of the entity to set
  172. """
  173. editor.EditorEntityAPIBus(bus.Event, "SetName", self.id, entity_name)
  174. def get_name(self) -> str:
  175. """
  176. Used to get the name of entity
  177. """
  178. return editor.EditorEntityInfoRequestBus(bus.Event, "GetName", self.id)
  179. def set_parent_entity(self, parent_entity_id):
  180. """
  181. Used to set this entity to be child of parent entity passed in
  182. :param: parent_entity_id: Entity Id of parent to set
  183. """
  184. assert (
  185. parent_entity_id.IsValid()
  186. ), f"Failure: Could not set parent to entity: {self.get_name()}, Invalid parent id"
  187. editor.EditorEntityAPIBus(bus.Event, "SetParent", self.id, parent_entity_id)
  188. def get_parent_id(self) -> azlmbr.entity.EntityId:
  189. """
  190. :return: Entity id of parent. Type: entity.EntityId()
  191. """
  192. return editor.EditorEntityInfoRequestBus(bus.Event, "GetParent", self.id)
  193. def add_component(self, component_name: str) -> EditorComponent:
  194. """
  195. Used to add new component to Entity.
  196. :param component_name: String of component name to add.
  197. :return: Component object of newly added component.
  198. """
  199. component = self.add_components([component_name])[0]
  200. return component
  201. def add_components(self, component_names: list) -> List[EditorComponent]:
  202. """
  203. Used to add multiple components
  204. :param: component_names: List of components to add to entity
  205. :return: List of newly added components to the entity
  206. """
  207. components = []
  208. type_ids = EditorComponent.get_type_ids(component_names)
  209. for type_id in type_ids:
  210. new_comp = EditorComponent()
  211. new_comp.type_id = type_id
  212. add_component_outcome = editor.EditorComponentAPIBus(
  213. bus.Broadcast, "AddComponentsOfType", self.id, [type_id]
  214. )
  215. assert (
  216. add_component_outcome.IsSuccess()
  217. ), f"Failure: Could not add component: '{new_comp.get_component_name()}' to entity: '{self.get_name()}'"
  218. new_comp.id = add_component_outcome.GetValue()[0]
  219. components.append(new_comp)
  220. return components
  221. def get_components_of_type(self, component_names: list) -> List[EditorComponent]:
  222. """
  223. Used to get components of type component_name that already exists on Entity
  224. :param component_name: Name to component to check
  225. :return: List of Entity Component objects of given component name
  226. """
  227. component_list = []
  228. type_ids = EditorComponent.get_type_ids(component_names)
  229. for type_id in type_ids:
  230. component = EditorComponent()
  231. component.type_id = type_id
  232. get_component_of_type_outcome = editor.EditorComponentAPIBus(
  233. bus.Broadcast, "GetComponentOfType", self.id, type_id
  234. )
  235. assert (
  236. get_component_of_type_outcome.IsSuccess()
  237. ), f"Failure: Entity: '{self.get_name()}' does not have component:'{component.get_component_name()}'"
  238. component.id = get_component_of_type_outcome.GetValue()
  239. component_list.append(component)
  240. return component_list
  241. def has_component(self, component_name: str) -> bool:
  242. """
  243. Used to verify if the entity has the specified component
  244. :param component_name: Name of component to check for
  245. :return: True, if entity has specified component. Else, False
  246. """
  247. type_ids = EditorComponent.get_type_ids([component_name])
  248. return editor.EditorComponentAPIBus(bus.Broadcast, "HasComponentOfType", self.id, type_ids[0])
  249. def get_start_status(self) -> int:
  250. """
  251. This will return a value for an entity's starting status (active, inactive, or editor) in the form of azlmbr.globals.property.EditorEntityStartStatus_<start type>. For comparisons using this value, should compare against the azlmbr property and not the int value
  252. """
  253. status = editor.EditorEntityInfoRequestBus(bus.Event, "GetStartStatus", self.id)
  254. if status == azlmbr.globals.property.EditorEntityStartStatus_StartActive:
  255. status_text = "active"
  256. elif status == azlmbr.globals.property.EditorEntityStartStatus_StartInactive:
  257. status_text = "inactive"
  258. elif status == azlmbr.globals.property.EditorEntityStartStatus_EditorOnly:
  259. status_text = "editor"
  260. utils.Report.info(f"The start status for {self.get_name} is {status_text}")
  261. self.start_status = status
  262. return status
  263. def set_start_status(self, desired_start_status: str):
  264. """
  265. Set an entity as active/inactive at beginning of runtime or it is editor-only,
  266. given its entity id and the start status then return set success
  267. :param desired_start_status: must be one of three choices: active, inactive, or editor
  268. """
  269. if desired_start_status == "active":
  270. status_to_set = azlmbr.globals.property.EditorEntityStartStatus_StartActive
  271. elif desired_start_status == "inactive":
  272. status_to_set = azlmbr.globals.property.EditorEntityStartStatus_StartInactive
  273. elif desired_start_status == "editor":
  274. status_to_set = azlmbr.globals.property.EditorEntityStartStatus_EditorOnly
  275. else:
  276. utils.Report.info(
  277. f"Invalid desired_start_status argument for {self.get_name} set_start_status command;\
  278. Use editor, active, or inactive"
  279. )
  280. editor.EditorEntityAPIBus(bus.Event, "SetStartStatus", self.id, status_to_set)
  281. set_status = self.get_start_status()
  282. assert set_status == status_to_set, f"Failed to set start status of {desired_start_status} to {self.get_name}"