123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- """
- All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
- its licensors.
- For complete copyright and license terms please see the LICENSE at the root of this
- distribution (the "License"). All use of this software is governed by the License,
- or, if provided, by the license below or the license accompanying this file. Do not
- remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- """
- import azlmbr.bus as bus
- import azlmbr.editor as editor
- import azlmbr.entity as entity
- import azlmbr.object
- from typing import List
- from math import isclose
- import collections.abc
- def find_entity_by_name(entity_name):
- """
- Gets an entity ID from the entity with the given entity_name
- :param entity_name: String of entity name to search for
- :return entity ID
- """
- search_filter = entity.SearchFilter()
- search_filter.names = [entity_name]
- matching_entity_list = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)
- if matching_entity_list:
- matching_entity = matching_entity_list[0]
- if matching_entity.IsValid():
- print(f'{entity_name} entity found with ID {matching_entity.ToString()}')
- return matching_entity
- else:
- return matching_entity_list
- def get_component_type_id(component_name):
- """
- Gets the component_type_id from a given component name
- :param component_name: String of component name to search for
- :return component type ID
- """
- type_ids_list = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType', [component_name],
- entity.EntityType().Game)
- component_type_id = type_ids_list[0]
- return component_type_id
- def add_level_component(component_name):
- """
- Adds the specified component to the Level Inspector
- :param component_name: String of component name to search for
- :return Component object.
- """
- level_component_list = [component_name]
- level_component_type_ids_list = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType',
- level_component_list, entity.EntityType().Level)
- level_component_outcome = editor.EditorLevelComponentAPIBus(bus.Broadcast, 'AddComponentsOfType',
- [level_component_type_ids_list[0]])
- level_component = level_component_outcome.GetValue()[0]
- return level_component
- def add_component(componentName, entityId):
- """
- Given a component name, finds component TypeId, adds to given entity, and verifies successful add/active state.
- :param componentName: String of component name to add.
- :param entityId: Entity to add component to.
- :return: Component object.
- """
- typeIdsList = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType', [componentName],
- entity.EntityType().Game)
- typeNamesList = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeNames', typeIdsList)
- componentOutcome = editor.EditorComponentAPIBus(bus.Broadcast, 'AddComponentsOfType', entityId, typeIdsList)
- isActive = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', componentOutcome.GetValue()[0])
- hasComponent = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', entityId, typeIdsList[0])
- if componentOutcome.IsSuccess() and isActive:
- print('{} component was added to entity'.format(typeNamesList[0]))
- elif componentOutcome.IsSuccess() and not isActive:
- print('{} component was added to entity, but the component is disabled'.format(typeNamesList[0]))
- elif not componentOutcome.IsSuccess():
- print('Failed to add {} component to entity'.format(typeNamesList[0]))
- if hasComponent:
- print('Entity has a {} component'.format(typeNamesList[0]))
- return componentOutcome.GetValue()[0]
- def add_component_of_type(componentTypeId, entityId):
- typeIdsList = [componentTypeId]
- componentOutcome = editor.EditorComponentAPIBus(
- azlmbr.bus.Broadcast, 'AddComponentsOfType', entityId, typeIdsList)
- return componentOutcome.GetValue()[0]
- def remove_component(component_name, entity_id):
- """
- Removes the specified component from the specified entity.
- :param component_name: String of component name to remove.
- :param entity_id: Entity to remove component from.
- :return: EntityComponentIdPair if removal was successful, else None.
- """
- type_ids_list = [get_component_type_id(component_name)]
- outcome = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', entity_id, type_ids_list[0])
- if outcome.IsSuccess():
- component_entity_pair = outcome.GetValue()
- editor.EditorComponentAPIBus(bus.Broadcast, 'RemoveComponents', [component_entity_pair])
- has_component = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', entity_id, type_ids_list[0])
- if has_component:
- print(f"Failed to remove {component_name}")
- return None
- else:
- print(f"{component_name} was successfully removed")
- return component_entity_pair
- else:
- print(f"{component_name} not found on entity")
- return None
- def get_component_property_value(component, component_propertyPath):
- """
- Given a component name and component property path, outputs the property's value.
- :param component: Component object to act on.
- :param componentPropertyPath: String of component property. (e.g. 'Settings|Visible')
- :return: Value set in given componentPropertyPath
- """
- componentPropertyObj = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentProperty', component,
- component_propertyPath)
- if componentPropertyObj.IsSuccess():
- componentProperty = componentPropertyObj.GetValue()
- print(f'{component_propertyPath} set to {componentProperty}')
- return componentProperty
- else:
- print(f'FAILURE: Could not get value from {component_propertyPath}')
- return None
- def get_property_tree(component):
- """
- Given a configured component object, prints the property tree info from that component
- :param component: Component object to act on.
- """
- pteObj = editor.EditorComponentAPIBus(bus.Broadcast, 'BuildComponentPropertyTreeEditor', component)
- pte = pteObj.GetValue()
- print(pte.build_paths_list())
- return pte
- def compare_values(first_object: object, second_object: object, name: str) -> bool:
- # Quick case - can we just directly compare the two objects successfully?
- if (first_object == second_object):
- result = True
- # No, so get a lot more specific
- elif isinstance(first_object, collections.abc.Container):
- # If they aren't both containers, they're different
- if not isinstance(second_object, collections.abc.Container):
- result = False
- # If they have different lengths, they're different
- elif len(first_object) != len (second_object):
- result = False
- # If they're different strings, they're containers but they failed the == check so
- # we know they're different
- elif isinstance(first_object, str):
- result = False
- else:
- # It's a collection of values, so iterate through them all...
- collection_idx = 0
- result = True
- for val1, val2 in zip(first_object, second_object):
- result = result and compare_values(val1, val2, f"{name} (index [{collection_idx}])")
- collection_idx = collection_idx + 1
- else:
- # Do approximate comparisons for floats
- if isinstance(first_object, float) and isclose(first_object, second_object, rel_tol=0.001):
- result = True
- # We currently don't have a generic way to compare PythonProxyObject contents, so return a
- # false positive result for now.
- elif isinstance(first_object, azlmbr.object.PythonProxyObject):
- print(f"{name}: validation inconclusive, the two objects cannot be directly compared.")
- result = True
- else:
- result = False
- if not result:
- print(f"compare_values failed: {first_object} ({type(first_object)}) vs {second_object} ({type(second_object)})")
- print(f"{name}: {'SUCCESS' if result else 'FAILURE'}")
- return result
- class Entity:
- """
- Entity class used to create entity objects
- :param name: String for the name of the Entity
- :param id: The ID of the entity
- """
- def __init__(self, name: str, id: object = entity.EntityId()):
- self.name: str = name
- self.id: object = id
- self.components: List[object] = None
- self.parent_id = None
- self.parent_name = None
- def create_entity(self, entity_position, components, parent_id=entity.EntityId()):
- self.id = editor.ToolsApplicationRequestBus(
- bus.Broadcast, "CreateNewEntityAtPosition", entity_position, parent_id
- )
- if self.id.IsValid():
- print(f"{self.name} Entity successfully created")
- editor.EditorEntityAPIBus(bus.Event, 'SetName', self.id, self.name)
- self.components = []
- for component in components:
- self.add_component(component)
- def add_component(self, component):
- new_component = add_component(component, self.id)
- self.components.append(new_component)
- def add_component_of_type(self, componentTypeId):
- new_component = add_component_of_type(componentTypeId, self.id)
- self.components.append(new_component)
- def remove_component(self, component):
- removed_component = remove_component(component, self.id)
- if removed_component is not None:
- self.components.remove(removed_component)
- def get_parent_info(self):
- """
- Sets the value for parent_id and parent_name on the entity (self)
- Prints the string for papertrail
- :return: None
- """
- self.parent_id = editor.EditorEntityInfoRequestBus(bus.Event, "GetParent", self.id)
- self.parent_name = editor.EditorEntityInfoRequestBus(bus.Event, "GetName", self.parent_id)
- print(f"The parent entity of {self.name} is {self.parent_name}")
- def set_test_parent_entity(self, parent_entity_obj):
- editor.EditorEntityAPIBus(bus.Event, "SetParent", self.id, parent_entity_obj.id)
- self.get_parent_info()
- def get_set_test(self, component_index: int, path: str, value: object, expected_result: object = None) -> bool:
- """
- Used to set and validate changes in component values
- :param component_index: Index location in the self.components list
- :param path: asset path in the component
- :param value: new value for the variable being changed in the component
- :param expected_result: (optional) check the result against a specific expected value
- """
- if expected_result is None:
- expected_result = value
- # Test Get/Set (get old value, set new value, check that new value was set correctly)
- print(f"Entity {self.name} Path {path} Component Index {component_index} ")
- component = self.components[component_index]
- old_value = get_component_property_value(component, path)
- if old_value is not None:
- print(f"SUCCESS: Retrieved property Value for {self.name}")
- else:
- print(f"FAILURE: Failed to find value in {self.name} {path}")
- return False
- if old_value == expected_result:
- print((f"WARNING: get_set_test on {self.name} is setting the same value that already exists ({old_value})."
- "The set results will be inconclusive."))
- editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", component, path, value)
- new_value = get_component_property_value(self.components[component_index], path)
- if new_value is not None:
- print(f"SUCCESS: Retrieved new property Value for {self.name}")
- else:
- print(f"FAILURE: Failed to find new value in {self.name}")
- return False
- return compare_values(new_value, expected_result, f"{self.name} {path}")
- def get_set_test(entity: object, component_index: int, path: str, value: object) -> bool:
- """
- Used to set and validate changes in component values
- :param component_index: Index location in the entity.components list
- :param path: asset path in the component
- :param value: new value for the variable being changed in the component
- """
- return entity.get_set_test(component_index, path, value)
- def get_set_property_test(ly_object: object, attribute_name: str, value: object, expected_result: object = None) -> bool:
- """
- Used to set and validate BehaviorContext property changes in Lumberyard objects
- :param ly_object: The lumberyard object to test
- :param attribute_name: property (attribute) name in the BehaviorContext
- :param value: new value for the variable being changed in the component
- :param expected_result: (optional) check the result against a specific expected value other than the one set
- """
- if expected_result is None:
- expected_result = value
- # Test Get/Set (get old value, set new value, check that new value was set correctly)
- print(f"Attempting to set {ly_object.typename}.{attribute_name} = {value} (expected result is {expected_result})")
- if hasattr(ly_object, attribute_name):
- print(f"SUCCESS: Located attribute {attribute_name} for {ly_object.typename}")
- else:
- print(f"FAILURE: Failed to find attribute {attribute_name} in {ly_object.typename}")
- return False
- old_value = getattr(ly_object, attribute_name)
- if old_value is not None:
- print(f"SUCCESS: Retrieved existing value {old_value} for {attribute_name} in {ly_object.typename}")
- else:
- print(f"FAILURE: Failed to retrieve value for {attribute_name} in {ly_object.typename}")
- return False
- if old_value == expected_result:
- print((f"WARNING: get_set_test on {attribute_name} is setting the same value that already exists ({old_value})."
- "The 'set' result for the test will be inconclusive."))
- setattr(ly_object, attribute_name, expected_result)
- new_value = getattr(ly_object, attribute_name)
- if new_value is not None:
- print(f"SUCCESS: Retrieved new value {new_value} for {attribute_name} in {ly_object.typename}")
- else:
- print(f"FAILURE: Failed to retrieve value for {attribute_name} in {ly_object.typename}")
- return False
- return compare_values(new_value, expected_result, f"{ly_object.typename}.{attribute_name}")
- def has_components(entity_id: object, component_list: list) -> bool:
- """
- Used to verify if a given entity has all the components of components_list. Returns True if all the
- components are present, else False
- :param entity_id: entity id of the entity
- :param component_list: list of component names to be verified
- """
- typeIdsList = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType', component_list,
- entity.EntityType().Game)
- for type_id in typeIdsList:
- if not editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', entity_id, type_id):
- return False
- return True
- class PathNotFoundError(Exception):
- def __init__(self, path):
- self.path = path
- def __str__(self):
- return f"Path \"{self.path}\" not found in Editor Settings"
- def get_editor_settings_path_list():
- """
- Get the list of Editor Settings paths
- """
- paths = editor.EditorSettingsAPIBus(bus.Broadcast, 'BuildSettingsList')
- return paths
- def get_editor_settings_by_path(path):
- """
- Get the value of Editor Settings based on the path.
- :param path: path to the Editor Settings to get the value
- """
- if path not in get_editor_settings_path_list():
- raise PathNotFoundError(path)
- outcome = editor.EditorSettingsAPIBus(bus.Broadcast, 'GetValue', path)
- if outcome.isSuccess():
- return outcome.GetValue()
- raise RuntimeError(f"GetValue for path '{path}' failed")
- def set_editor_settings_by_path(path, value, is_bool = False):
- """
- Set the value of Editor Settings based on the path.
- # NOTE: Some Editor Settings may need an Editor restart to apply.
- # Ex: Enabling or disabling New Viewport Interaction Model
- :param path: path to the Editor Settings to get the value
- :param value: value to be set
- :param is_bool: True for Boolean settings (enable/disable), False for other settings
- """
- if path not in get_editor_settings_path_list():
- raise PathNotFoundError(path)
- if is_bool and not isinstance(value, bool):
- def ParseBoolValue(value):
- if(value == "0"):
- return False
- return True
- value = ParseBoolValue(value)
- outcome = editor.EditorSettingsAPIBus(bus.Broadcast, 'SetValue', path, value)
- if not outcome.isSuccess():
- raise RuntimeError(f"SetValue for path '{path}' failed")
- print(f"Value for path '{path}' is set to {value}")
- def get_component_type_id_map(component_name_list):
- """
- Given a list of component names, returns a map of component name -> component type id
- :param component_name_list: The lumberyard object to test
- :return: Dictionary of component name -> component type id pairs
- """
- # Remove any duplicates so we don't have to query for the same TypeId
- component_names = list(set(component_name_list))
- type_ids_by_component = {}
- type_ids = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType', component_names,
- entity.EntityType().Game)
- for i, typeId in enumerate(type_ids):
- type_ids_by_component[component_names[i]] = typeId
- return type_ids_by_component
|