GitHub
5 年前
当前提交
aae58330
共有 105 个文件被更改,包括 4886 次插入 和 3675 次删除
-
4.yamato/protobuf-generation-test.yml
-
6.yamato/standalone-build-test.yml
-
11.yamato/training-int-tests.yml
-
3Project/Assets/ML-Agents/Examples/SharedAssets/Scripts/SensorBase.cs
-
7Project/Assets/ML-Agents/Examples/Tennis/Scripts/TennisAgent.cs
-
2README.md
-
6com.unity.ml-agents/CHANGELOG.md
-
21com.unity.ml-agents/Runtime/Academy.cs
-
17com.unity.ml-agents/Runtime/Agent.cs
-
5com.unity.ml-agents/Runtime/Communicator/RpcCommunicator.cs
-
34com.unity.ml-agents/Runtime/DecisionRequester.cs
-
3com.unity.ml-agents/Runtime/Sensors/CameraSensor.cs
-
6com.unity.ml-agents/Runtime/Sensors/ISensor.cs
-
3com.unity.ml-agents/Runtime/Sensors/RayPerceptionSensor.cs
-
3com.unity.ml-agents/Runtime/Sensors/RenderTextureSensor.cs
-
13com.unity.ml-agents/Runtime/Sensors/StackingSensor.cs
-
6com.unity.ml-agents/Runtime/Sensors/VectorSensor.cs
-
44com.unity.ml-agents/Runtime/SideChannels/IncomingMessage.cs
-
56com.unity.ml-agents/Tests/Editor/MLAgentsEditModeTest.cs
-
1com.unity.ml-agents/Tests/Editor/ParameterLoaderTest.cs
-
102com.unity.ml-agents/Tests/Editor/PublicAPI/PublicApiValidation.cs
-
1com.unity.ml-agents/Tests/Editor/Sensor/FloatVisualSensorTests.cs
-
1com.unity.ml-agents/Tests/Editor/Sensor/SensorShapeValidatorTests.cs
-
18com.unity.ml-agents/Tests/Editor/Sensor/StackingSensorTests.cs
-
26com.unity.ml-agents/Tests/Editor/SideChannelTests.cs
-
16com.unity.ml-agents/package.json
-
6config/trainer_config.yaml
-
3docs/Getting-Started.md
-
1docs/Learning-Environment-Examples.md
-
10docs/ML-Agents-Overview.md
-
4docs/Migrating.md
-
141docs/Python-API.md
-
10docs/Training-ML-Agents.md
-
11docs/Training-PPO.md
-
11docs/Training-SAC.md
-
98docs/Training-Self-Play.md
-
999docs/images/3dball_big.png
-
852docs/images/3dball_small.png
-
974docs/images/curriculum.png
-
999docs/images/ml-agents-LSTM.png
-
181docs/images/monitor.png
-
15gym-unity/README.md
-
342gym-unity/gym_unity/envs/__init__.py
-
198gym-unity/gym_unity/tests/test_gym.py
-
249ml-agents-envs/mlagents_envs/base_env.py
-
104ml-agents-envs/mlagents_envs/environment.py
-
99ml-agents-envs/mlagents_envs/rpc_utils.py
-
38ml-agents-envs/mlagents_envs/side_channel/incoming_message.py
-
43ml-agents-envs/mlagents_envs/tests/test_envs.py
-
170ml-agents-envs/mlagents_envs/tests/test_rpc_utils.py
-
20ml-agents-envs/mlagents_envs/tests/test_side_channel.py
-
180ml-agents/mlagents/trainers/agent_processor.py
-
52ml-agents/mlagents/trainers/behavior_id_utils.py
-
16ml-agents/mlagents/trainers/brain_conversion_utils.py
-
73ml-agents/mlagents/trainers/demo_loader.py
-
37ml-agents/mlagents/trainers/env_manager.py
-
453ml-agents/mlagents/trainers/ghost/trainer.py
-
20ml-agents/mlagents/trainers/learn.py
-
10ml-agents/mlagents/trainers/policy/nn_policy.py
-
4ml-agents/mlagents/trainers/policy/policy.py
-
85ml-agents/mlagents/trainers/policy/tf_policy.py
-
11ml-agents/mlagents/trainers/ppo/trainer.py
-
9ml-agents/mlagents/trainers/sac/trainer.py
-
26ml-agents/mlagents/trainers/simple_env_manager.py
-
18ml-agents/mlagents/trainers/stats.py
-
24ml-agents/mlagents/trainers/subprocess_env_manager.py
-
42ml-agents/mlagents/trainers/tests/mock_brain.py
-
137ml-agents/mlagents/trainers/tests/simple_test_envs.py
-
54ml-agents/mlagents/trainers/tests/test_agent_processor.py
-
32ml-agents/mlagents/trainers/tests/test_demo_loader.py
-
48ml-agents/mlagents/trainers/tests/test_ghost.py
-
2ml-agents/mlagents/trainers/tests/test_learn.py
-
55ml-agents/mlagents/trainers/tests/test_nn_policy.py
-
24ml-agents/mlagents/trainers/tests/test_policy.py
-
4ml-agents/mlagents/trainers/tests/test_ppo.py
-
6ml-agents/mlagents/trainers/tests/test_sac.py
-
59ml-agents/mlagents/trainers/tests/test_simple_rl.py
-
7ml-agents/mlagents/trainers/tests/test_stats.py
-
5ml-agents/mlagents/trainers/tests/test_subprocess_env_manager.py
-
12ml-agents/mlagents/trainers/tests/test_trainer_util.py
-
9ml-agents/mlagents/trainers/trainer/trainer.py
-
11ml-agents/mlagents/trainers/trainer_controller.py
-
33ml-agents/mlagents/trainers/trainer_util.py
-
18ml-agents/tests/yamato/standalone_build_tests.py
-
13ml-agents/tests/yamato/training_int_tests.py
-
58ml-agents/tests/yamato/yamato_utils.py
-
1utils/make_readme_table.py
-
32.yamato/gym-interface-test.yml
-
32.yamato/python-ll-api-test.yml
-
129com.unity.ml-agents/Tests/Runtime/RuntimeAPITest.cs
-
11com.unity.ml-agents/Tests/Runtime/RuntimeAPITest.cs.meta
-
25com.unity.ml-agents/Tests/Runtime/Unity.ML-Agents.Runtime.Tests.asmdef
-
7com.unity.ml-agents/Tests/Runtime/Unity.ML-Agents.Runtime.Tests.asmdef.meta
-
92ml-agents/mlagents/trainers/ghost/controller.py
-
21ml-agents/tests/yamato/setup_venv.py
-
41ml-agents/tests/yamato/scripts/run_gym.py
-
94ml-agents/tests/yamato/scripts/run_llapi.py
-
429com.unity.ml-agents/Tests/Runtime/SerializeTestScene.unity
-
7com.unity.ml-agents/Tests/Runtime/SerializeTestScene.unity.meta
|
|||
{ |
|||
"name": "com.unity.ml-agents", |
|||
"displayName":"ML Agents", |
|||
"version": "0.15.0-preview", |
|||
"unity": "2018.4", |
|||
"description": "Add interactivity to your game with Machine Learning Agents trained using Deep Reinforcement Learning.", |
|||
"dependencies": { |
|||
"com.unity.barracuda": "0.6.1-preview" |
|||
} |
|||
"name": "com.unity.ml-agents", |
|||
"displayName": "ML Agents", |
|||
"version": "0.15.1-preview", |
|||
"unity": "2018.4", |
|||
"description": "Add interactivity to your game with Machine Learning Agents trained using Deep Reinforcement Learning.", |
|||
"dependencies": { |
|||
"com.unity.barracuda": "0.6.1-preview" |
|||
} |
|||
} |
999
docs/images/3dball_big.png
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
999
docs/images/ml-agents-LSTM.png
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
from typing import Dict, NamedTuple |
|||
from typing import NamedTuple |
|||
from urllib.parse import urlparse, parse_qs |
|||
name_behavior_id: str |
|||
""" |
|||
BehaviorIdentifiers is a named tuple of the identifiers that uniquely distinguish |
|||
an agent encountered in the trainer_controller. The named tuple consists of the |
|||
fully qualified behavior name, the name of the brain name (corresponds to trainer |
|||
in the trainer controller) and the team id. In the future, this can be extended |
|||
to support further identifiers. |
|||
""" |
|||
|
|||
behavior_id: str |
|||
behavior_ids: Dict[str, int] |
|||
team_id: int |
|||
Parses a name_behavior_id of the form name?team=0¶m1=i&... |
|||
Parses a name_behavior_id of the form name?team=0 |
|||
This allows you to access the brain name and distinguishing identifiers |
|||
without parsing more than once. |
|||
This allows you to access the brain name and team id of an agent |
|||
ids: Dict[str, int] = {} |
|||
if "?" in name_behavior_id: |
|||
name, identifiers = name_behavior_id.rsplit("?", 1) |
|||
if "&" in identifiers: |
|||
list_of_identifiers = identifiers.split("&") |
|||
else: |
|||
list_of_identifiers = [identifiers] |
|||
|
|||
for identifier in list_of_identifiers: |
|||
key, value = identifier.split("=") |
|||
ids[key] = int(value) |
|||
else: |
|||
name = name_behavior_id |
|||
|
|||
parsed = urlparse(name_behavior_id) |
|||
name = parsed.path |
|||
ids = parse_qs(parsed.query) |
|||
team_id: int = 0 |
|||
if "team" in ids: |
|||
team_id = int(ids["team"][0]) |
|||
name_behavior_id=name_behavior_id, brain_name=name, behavior_ids=ids |
|||
behavior_id=name_behavior_id, brain_name=name, team_id=team_id |
|||
|
|||
|
|||
def create_name_behavior_id(name: str, team_id: int) -> str: |
|||
""" |
|||
Reconstructs fully qualified behavior name from name and team_id |
|||
:param name: brain name |
|||
:param team_id: team ID |
|||
:return: name_behavior_id |
|||
""" |
|||
return name + "?team=" + str(team_id) |
|
|||
test_editors: |
|||
- version: 2019.3 |
|||
--- |
|||
{% for editor in test_editors %} |
|||
test_gym_interface_{{ editor.version }}: |
|||
name: Test Mac Gym Interface {{ editor.version }} |
|||
agent: |
|||
type: Unity::VM::osx |
|||
image: ml-agents/ml-agents-bokken-mac:0.1.4-492264 |
|||
flavor: b1.small |
|||
variables: |
|||
UNITY_VERSION: {{ editor.version }} |
|||
commands: |
|||
- pip install pyyaml |
|||
- python -u -m ml-agents.tests.yamato.setup_venv |
|||
- ./venv/bin/python ml-agents/tests/yamato/scripts/run_gym.py --env=artifacts/testPlayer-Basic |
|||
dependencies: |
|||
- .yamato/standalone-build-test.yml#test_mac_standalone_{{ editor.version }} |
|||
triggers: |
|||
cancel_old_ci: true |
|||
changes: |
|||
only: |
|||
- "com.unity.ml-agents/**" |
|||
- "Project/**" |
|||
- "ml-agents/**" |
|||
- "ml-agents-envs/**" |
|||
- ".yamato/gym-interface-test.yml" |
|||
except: |
|||
- "*.md" |
|||
- "com.unity.ml-agents/*.md" |
|||
- "com.unity.ml-agents/**/*.md" |
|||
{% endfor %} |
|
|||
test_editors: |
|||
- version: 2019.3 |
|||
--- |
|||
{% for editor in test_editors %} |
|||
test_mac_ll_api_{{ editor.version }}: |
|||
name: Test Mac LL-API {{ editor.version }} |
|||
agent: |
|||
type: Unity::VM::osx |
|||
image: ml-agents/ml-agents-bokken-mac:0.1.4-492264 |
|||
flavor: b1.small |
|||
variables: |
|||
UNITY_VERSION: {{ editor.version }} |
|||
commands: |
|||
- pip install pyyaml |
|||
- python -u -m ml-agents.tests.yamato.setup_venv |
|||
- ./venv/bin/python ml-agents/tests/yamato/scripts/run_llapi.py |
|||
dependencies: |
|||
- .yamato/standalone-build-test.yml#test_mac_standalone_{{ editor.version }} |
|||
triggers: |
|||
cancel_old_ci: true |
|||
changes: |
|||
only: |
|||
- "com.unity.ml-agents/**" |
|||
- "Project/**" |
|||
- "ml-agents/**" |
|||
- "ml-agents-envs/**" |
|||
- ".yamato/python-ll-api-test.yml" |
|||
except: |
|||
- "*.md" |
|||
- "com.unity.ml-agents/*.md" |
|||
- "com.unity.ml-agents/**/*.md" |
|||
{% endfor %} |
|
|||
#if UNITY_INCLUDE_TESTS
|
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using MLAgents; |
|||
using MLAgents.Policies; |
|||
using MLAgents.Sensors; |
|||
using NUnit.Framework; |
|||
using UnityEngine; |
|||
using UnityEngine.TestTools; |
|||
|
|||
namespace Tests |
|||
{ |
|||
|
|||
public class PublicApiAgent : Agent |
|||
{ |
|||
public int numHeuristicCalls; |
|||
|
|||
public override float[] Heuristic() |
|||
{ |
|||
numHeuristicCalls++; |
|||
return base.Heuristic(); |
|||
} |
|||
}// Simple SensorComponent that sets up a StackingSensor
|
|||
public class StackingComponent : SensorComponent |
|||
{ |
|||
public SensorComponent wrappedComponent; |
|||
public int numStacks; |
|||
|
|||
public override ISensor CreateSensor() |
|||
{ |
|||
var wrappedSensor = wrappedComponent.CreateSensor(); |
|||
return new StackingSensor(wrappedSensor, numStacks); |
|||
} |
|||
|
|||
public override int[] GetObservationShape() |
|||
{ |
|||
int[] shape = (int[]) wrappedComponent.GetObservationShape().Clone(); |
|||
for (var i = 0; i < shape.Length; i++) |
|||
{ |
|||
shape[i] *= numStacks; |
|||
} |
|||
|
|||
return shape; |
|||
} |
|||
} |
|||
|
|||
public class RuntimeApiTest |
|||
{ |
|||
[SetUp] |
|||
public static void Setup() |
|||
{ |
|||
Academy.Instance.AutomaticSteppingEnabled = false; |
|||
} |
|||
|
|||
[UnityTest] |
|||
public IEnumerator RuntimeApiTestWithEnumeratorPasses() |
|||
{ |
|||
var gameObject = new GameObject(); |
|||
|
|||
var behaviorParams = gameObject.AddComponent<BehaviorParameters>(); |
|||
behaviorParams.brainParameters.vectorObservationSize = 3; |
|||
behaviorParams.brainParameters.numStackedVectorObservations = 2; |
|||
behaviorParams.brainParameters.vectorActionDescriptions = new[] { "TestActionA", "TestActionB" }; |
|||
behaviorParams.brainParameters.vectorActionSize = new[] { 2, 2 }; |
|||
behaviorParams.brainParameters.vectorActionSpaceType = SpaceType.Discrete; |
|||
behaviorParams.behaviorName = "TestBehavior"; |
|||
behaviorParams.TeamId = 42; |
|||
behaviorParams.useChildSensors = true; |
|||
|
|||
|
|||
// Can't actually create an Agent with InferenceOnly and no model, so change back
|
|||
behaviorParams.behaviorType = BehaviorType.Default; |
|||
|
|||
var sensorComponent = gameObject.AddComponent<RayPerceptionSensorComponent3D>(); |
|||
sensorComponent.sensorName = "ray3d"; |
|||
sensorComponent.detectableTags = new List<string> { "Player", "Respawn" }; |
|||
sensorComponent.raysPerDirection = 3; |
|||
|
|||
// Make a StackingSensor that wraps the RayPerceptionSensorComponent3D
|
|||
// This isn't necessarily practical, just to ensure that it can be done
|
|||
var wrappingSensorComponent = gameObject.AddComponent<StackingComponent>(); |
|||
wrappingSensorComponent.wrappedComponent = sensorComponent; |
|||
wrappingSensorComponent.numStacks = 3; |
|||
|
|||
// ISensor isn't set up yet.
|
|||
Assert.IsNull(sensorComponent.raySensor); |
|||
|
|||
|
|||
// Make sure we can set the behavior type correctly after the agent is initialized
|
|||
// (this creates a new policy).
|
|||
behaviorParams.behaviorType = BehaviorType.HeuristicOnly; |
|||
|
|||
// Agent needs to be added after everything else is setup.
|
|||
var agent = gameObject.AddComponent<PublicApiAgent>(); |
|||
|
|||
// DecisionRequester has to be added after Agent.
|
|||
var decisionRequester = gameObject.AddComponent<DecisionRequester>(); |
|||
decisionRequester.DecisionPeriod = 2; |
|||
decisionRequester.TakeActionsBetweenDecisions = true; |
|||
|
|||
|
|||
// Initialization should set up the sensors
|
|||
Assert.IsNotNull(sensorComponent.raySensor); |
|||
|
|||
// Let's change the inference device
|
|||
var otherDevice = behaviorParams.inferenceDevice == InferenceDevice.CPU ? InferenceDevice.GPU : InferenceDevice.CPU; |
|||
agent.SetModel(behaviorParams.behaviorName, behaviorParams.model, otherDevice); |
|||
|
|||
agent.AddReward(1.0f); |
|||
|
|||
// skip a frame.
|
|||
yield return null; |
|||
|
|||
Academy.Instance.EnvironmentStep(); |
|||
|
|||
var actions = agent.GetAction(); |
|||
// default Heuristic implementation should return zero actions.
|
|||
Assert.AreEqual(new[] {0.0f, 0.0f}, actions); |
|||
Assert.AreEqual(1, agent.numHeuristicCalls); |
|||
|
|||
Academy.Instance.EnvironmentStep(); |
|||
Assert.AreEqual(1, agent.numHeuristicCalls); |
|||
|
|||
Academy.Instance.EnvironmentStep(); |
|||
Assert.AreEqual(2, agent.numHeuristicCalls); |
|||
} |
|||
} |
|||
} |
|||
#endif
|
|
|||
fileFormatVersion: 2 |
|||
guid: 17878576e4ed14b09875e37394e5ad90 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
{ |
|||
"name": "Tests", |
|||
"references": [ |
|||
"Unity.ML-Agents", |
|||
"Barracuda", |
|||
"Unity.ML-Agents.CommunicatorObjects", |
|||
"Unity.ML-Agents.Editor" |
|||
], |
|||
"optionalUnityReferences": [ |
|||
"TestAssemblies" |
|||
], |
|||
"includePlatforms": [], |
|||
"excludePlatforms": [], |
|||
"allowUnsafeCode": false, |
|||
"overrideReferences": true, |
|||
"precompiledReferences": [ |
|||
"System.IO.Abstractions.dll", |
|||
"System.IO.Abstractions.TestingHelpers.dll", |
|||
"Google.Protobuf.dll" |
|||
], |
|||
"autoReferenced": false, |
|||
"defineConstraints": [ |
|||
"UNITY_INCLUDE_TESTS" |
|||
] |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: d29014db7ebcd4cf4a14f537fbf02110 |
|||
AssemblyDefinitionImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
from mlagents_envs.logging_util import get_logger |
|||
from typing import Deque, Dict |
|||
from collections import deque |
|||
from mlagents.trainers.ghost.trainer import GhostTrainer |
|||
|
|||
logger = get_logger(__name__) |
|||
|
|||
|
|||
class GhostController: |
|||
""" |
|||
GhostController contains a queue of team ids. GhostTrainers subscribe to the GhostController and query |
|||
it to get the current learning team. The GhostController cycles through team ids every 'swap_interval' |
|||
which corresponds to the number of trainer steps between changing learning teams. |
|||
The GhostController is a unique object and there can only be one per training run. |
|||
""" |
|||
|
|||
def __init__(self, maxlen: int = 10): |
|||
""" |
|||
Create a GhostController. |
|||
:param maxlen: Maximum number of GhostTrainers allowed in this GhostController |
|||
""" |
|||
|
|||
# Tracks last swap step for each learning team because trainer |
|||
# steps of all GhostTrainers do not increment together |
|||
self._queue: Deque[int] = deque(maxlen=maxlen) |
|||
self._learning_team: int = -1 |
|||
# Dict from team id to GhostTrainer for ELO calculation |
|||
self._ghost_trainers: Dict[int, GhostTrainer] = {} |
|||
|
|||
@property |
|||
def get_learning_team(self) -> int: |
|||
""" |
|||
Returns the current learning team. |
|||
:return: The learning team id |
|||
""" |
|||
return self._learning_team |
|||
|
|||
def subscribe_team_id(self, team_id: int, trainer: GhostTrainer) -> None: |
|||
""" |
|||
Given a team_id and trainer, add to queue and trainers if not already. |
|||
The GhostTrainer is used later by the controller to get ELO ratings of agents. |
|||
:param team_id: The team_id of an agent managed by this GhostTrainer |
|||
:param trainer: A GhostTrainer that manages this team_id. |
|||
""" |
|||
if team_id not in self._ghost_trainers: |
|||
self._ghost_trainers[team_id] = trainer |
|||
if self._learning_team < 0: |
|||
self._learning_team = team_id |
|||
else: |
|||
self._queue.append(team_id) |
|||
|
|||
def change_training_team(self, step: int) -> None: |
|||
""" |
|||
The current learning team is added to the end of the queue and then updated with the |
|||
next in line. |
|||
:param step: The step of the trainer for debugging |
|||
""" |
|||
self._queue.append(self._learning_team) |
|||
self._learning_team = self._queue.popleft() |
|||
logger.debug( |
|||
"Learning team {} swapped on step {}".format(self._learning_team, step) |
|||
) |
|||
|
|||
# Adapted from https://github.com/Unity-Technologies/ml-agents/pull/1975 and |
|||
# https://metinmediamath.wordpress.com/2013/11/27/how-to-calculate-the-elo-rating-including-example/ |
|||
# ELO calculation |
|||
# TODO : Generalize this to more than two teams |
|||
def compute_elo_rating_changes(self, rating: float, result: float) -> float: |
|||
""" |
|||
Calculates ELO. Given the rating of the learning team and result. The GhostController |
|||
queries the other GhostTrainers for the ELO of their agent that is currently being deployed. |
|||
Note, this could be the current agent or a past snapshot. |
|||
:param rating: Rating of the learning team. |
|||
:param result: Win, loss, or draw from the perspective of the learning team. |
|||
:return: The change in ELO. |
|||
""" |
|||
opponent_rating: float = 0.0 |
|||
for team_id, trainer in self._ghost_trainers.items(): |
|||
if team_id != self._learning_team: |
|||
opponent_rating = trainer.get_opponent_elo() |
|||
r1 = pow(10, rating / 400) |
|||
r2 = pow(10, opponent_rating / 400) |
|||
|
|||
summed = r1 + r2 |
|||
e1 = r1 / summed |
|||
|
|||
change = result - e1 |
|||
for team_id, trainer in self._ghost_trainers.items(): |
|||
if team_id != self._learning_team: |
|||
trainer.change_opponent_elo(change) |
|||
|
|||
return change |
|
|||
import argparse |
|||
|
|||
from .yamato_utils import init_venv |
|||
|
|||
|
|||
def main(): |
|||
parser = argparse.ArgumentParser() |
|||
parser.add_argument("--mlagents-version", default=None) |
|||
parser.add_argument("--extra-packages", default=None) |
|||
args = parser.parse_args() |
|||
extra_packages = [] |
|||
if args.extra_packages is not None: |
|||
extra_packages = args.extra_packages.split(",") |
|||
|
|||
init_venv( |
|||
mlagents_python_version=args.mlagents_version, extra_packages=extra_packages |
|||
) |
|||
|
|||
|
|||
if __name__ == "__main__": |
|||
main() |
|
|||
import argparse |
|||
|
|||
from gym_unity.envs import UnityEnv |
|||
|
|||
|
|||
def main(env_name): |
|||
""" |
|||
Run the gym test using the specified environment |
|||
:param env_name: Name of the Unity environment binary to launch |
|||
""" |
|||
env = UnityEnv(env_name, worker_id=1, use_visual=False, no_graphics=True) |
|||
|
|||
try: |
|||
# Examine environment parameters |
|||
print(str(env)) |
|||
|
|||
# Reset the environment |
|||
initial_observations = env.reset() |
|||
|
|||
if len(env.observation_space.shape) == 1: |
|||
# Examine the initial vector observation |
|||
print("Agent observations look like: \n{}".format(initial_observations)) |
|||
|
|||
for _episode in range(10): |
|||
env.reset() |
|||
done = False |
|||
episode_rewards = 0 |
|||
while not done: |
|||
actions = env.action_space.sample() |
|||
obs, reward, done, _ = env.step(actions) |
|||
episode_rewards += reward |
|||
print("Total reward this episode: {}".format(episode_rewards)) |
|||
finally: |
|||
env.close() |
|||
|
|||
|
|||
if __name__ == "__main__": |
|||
parser = argparse.ArgumentParser() |
|||
parser.add_argument("--env", default="Project/testPlayer") |
|||
args = parser.parse_args() |
|||
main(args.env) |
|
|||
import argparse |
|||
import numpy as np |
|||
|
|||
from mlagents_envs.environment import UnityEnvironment |
|||
from mlagents_envs.side_channel.engine_configuration_channel import ( |
|||
EngineConfigurationChannel, |
|||
) |
|||
|
|||
|
|||
def main(env_name): |
|||
""" |
|||
Run the low-level API test using the specified environment |
|||
:param env_name: Name of the Unity environment binary to launch |
|||
""" |
|||
engine_configuration_channel = EngineConfigurationChannel() |
|||
env = UnityEnvironment( |
|||
file_name=env_name, |
|||
side_channels=[engine_configuration_channel], |
|||
no_graphics=True, |
|||
args=["-logFile", "-"], |
|||
) |
|||
|
|||
try: |
|||
# Reset the environment |
|||
env.reset() |
|||
|
|||
# Set the default brain to work with |
|||
group_name = env.get_behavior_names()[0] |
|||
group_spec = env.get_behavior_spec(group_name) |
|||
|
|||
# Set the time scale of the engine |
|||
engine_configuration_channel.set_configuration_parameters(time_scale=3.0) |
|||
|
|||
# Get the state of the agents |
|||
decision_steps, terminal_steps = env.get_steps(group_name) |
|||
|
|||
# Examine the number of observations per Agent |
|||
print("Number of observations : ", len(group_spec.observation_shapes)) |
|||
|
|||
# Is there a visual observation ? |
|||
vis_obs = any(len(shape) == 3 for shape in group_spec.observation_shapes) |
|||
print("Is there a visual observation ?", vis_obs) |
|||
|
|||
# Examine the state space for the first observation for the first agent |
|||
print( |
|||
"First Agent observation looks like: \n{}".format(decision_steps.obs[0][0]) |
|||
) |
|||
|
|||
for _episode in range(10): |
|||
env.reset() |
|||
decision_steps, terminal_steps = env.get_steps(group_name) |
|||
done = False |
|||
episode_rewards = 0 |
|||
tracked_agent = -1 |
|||
while not done: |
|||
if group_spec.is_action_continuous(): |
|||
action = np.random.randn( |
|||
len(decision_steps), group_spec.action_size |
|||
) |
|||
|
|||
elif group_spec.is_action_discrete(): |
|||
branch_size = group_spec.discrete_action_branches |
|||
action = np.column_stack( |
|||
[ |
|||
np.random.randint( |
|||
0, branch_size[i], size=(len(decision_steps)) |
|||
) |
|||
for i in range(len(branch_size)) |
|||
] |
|||
) |
|||
else: |
|||
# Should never happen |
|||
action = None |
|||
if tracked_agent == -1 and len(decision_steps) > 1: |
|||
tracked_agent = decision_steps.agent_id[0] |
|||
env.set_actions(group_name, action) |
|||
env.step() |
|||
decision_steps, terminal_steps = env.get_steps(group_name) |
|||
done = False |
|||
if tracked_agent in decision_steps: |
|||
episode_rewards += decision_steps[tracked_agent].reward |
|||
if tracked_agent in terminal_steps: |
|||
episode_rewards += terminal_steps[tracked_agent].reward |
|||
done = True |
|||
print("Total reward this episode: {}".format(episode_rewards)) |
|||
finally: |
|||
env.close() |
|||
|
|||
|
|||
if __name__ == "__main__": |
|||
parser = argparse.ArgumentParser() |
|||
parser.add_argument("--env", default="artifacts/testPlayer") |
|||
args = parser.parse_args() |
|||
main(args.env) |
|
|||
%YAML 1.1 |
|||
%TAG !u! tag:unity3d.com,2011: |
|||
--- !u!29 &1 |
|||
OcclusionCullingSettings: |
|||
m_ObjectHideFlags: 0 |
|||
serializedVersion: 2 |
|||
m_OcclusionBakeSettings: |
|||
smallestOccluder: 5 |
|||
smallestHole: 0.25 |
|||
backfaceThreshold: 100 |
|||
m_SceneGUID: 00000000000000000000000000000000 |
|||
m_OcclusionCullingData: {fileID: 0} |
|||
--- !u!104 &2 |
|||
RenderSettings: |
|||
m_ObjectHideFlags: 0 |
|||
serializedVersion: 9 |
|||
m_Fog: 0 |
|||
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} |
|||
m_FogMode: 3 |
|||
m_FogDensity: 0.01 |
|||
m_LinearFogStart: 0 |
|||
m_LinearFogEnd: 300 |
|||
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} |
|||
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} |
|||
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} |
|||
m_AmbientIntensity: 1 |
|||
m_AmbientMode: 0 |
|||
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} |
|||
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} |
|||
m_HaloStrength: 0.5 |
|||
m_FlareStrength: 1 |
|||
m_FlareFadeSpeed: 3 |
|||
m_HaloTexture: {fileID: 0} |
|||
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} |
|||
m_DefaultReflectionMode: 0 |
|||
m_DefaultReflectionResolution: 128 |
|||
m_ReflectionBounces: 1 |
|||
m_ReflectionIntensity: 1 |
|||
m_CustomReflection: {fileID: 0} |
|||
m_Sun: {fileID: 0} |
|||
m_IndirectSpecularColor: {r: 0.44657898, g: 0.49641287, b: 0.5748173, a: 1} |
|||
m_UseRadianceAmbientProbe: 0 |
|||
--- !u!157 &3 |
|||
LightmapSettings: |
|||
m_ObjectHideFlags: 0 |
|||
serializedVersion: 11 |
|||
m_GIWorkflowMode: 0 |
|||
m_GISettings: |
|||
serializedVersion: 2 |
|||
m_BounceScale: 1 |
|||
m_IndirectOutputScale: 1 |
|||
m_AlbedoBoost: 1 |
|||
m_EnvironmentLightingMode: 0 |
|||
m_EnableBakedLightmaps: 1 |
|||
m_EnableRealtimeLightmaps: 1 |
|||
m_LightmapEditorSettings: |
|||
serializedVersion: 10 |
|||
m_Resolution: 2 |
|||
m_BakeResolution: 40 |
|||
m_AtlasSize: 1024 |
|||
m_AO: 0 |
|||
m_AOMaxDistance: 1 |
|||
m_CompAOExponent: 1 |
|||
m_CompAOExponentDirect: 0 |
|||
m_Padding: 2 |
|||
m_LightmapParameters: {fileID: 0} |
|||
m_LightmapsBakeMode: 1 |
|||
m_TextureCompression: 1 |
|||
m_FinalGather: 0 |
|||
m_FinalGatherFiltering: 1 |
|||
m_FinalGatherRayCount: 256 |
|||
m_ReflectionCompression: 2 |
|||
m_MixedBakeMode: 2 |
|||
m_BakeBackend: 1 |
|||
m_PVRSampling: 1 |
|||
m_PVRDirectSampleCount: 32 |
|||
m_PVRSampleCount: 500 |
|||
m_PVRBounces: 2 |
|||
m_PVRFilterTypeDirect: 0 |
|||
m_PVRFilterTypeIndirect: 0 |
|||
m_PVRFilterTypeAO: 0 |
|||
m_PVRFilteringMode: 1 |
|||
m_PVRCulling: 1 |
|||
m_PVRFilteringGaussRadiusDirect: 1 |
|||
m_PVRFilteringGaussRadiusIndirect: 5 |
|||
m_PVRFilteringGaussRadiusAO: 2 |
|||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5 |
|||
m_PVRFilteringAtrousPositionSigmaIndirect: 2 |
|||
m_PVRFilteringAtrousPositionSigmaAO: 1 |
|||
m_ShowResolutionOverlay: 1 |
|||
m_LightingDataAsset: {fileID: 0} |
|||
m_UseShadowmask: 1 |
|||
--- !u!196 &4 |
|||
NavMeshSettings: |
|||
serializedVersion: 2 |
|||
m_ObjectHideFlags: 0 |
|||
m_BuildSettings: |
|||
serializedVersion: 2 |
|||
agentTypeID: 0 |
|||
agentRadius: 0.5 |
|||
agentHeight: 2 |
|||
agentSlope: 45 |
|||
agentClimb: 0.4 |
|||
ledgeDropHeight: 0 |
|||
maxJumpAcrossDistance: 0 |
|||
minRegionArea: 2 |
|||
manualCellSize: 0 |
|||
cellSize: 0.16666667 |
|||
manualTileSize: 0 |
|||
tileSize: 256 |
|||
accuratePlacement: 0 |
|||
debug: |
|||
m_Flags: 0 |
|||
m_NavMeshData: {fileID: 0} |
|||
--- !u!1 &106586301 |
|||
GameObject: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
serializedVersion: 6 |
|||
m_Component: |
|||
- component: {fileID: 106586304} |
|||
- component: {fileID: 106586303} |
|||
- component: {fileID: 106586302} |
|||
m_Layer: 0 |
|||
m_Name: Agent |
|||
m_TagString: Untagged |
|||
m_Icon: {fileID: 0} |
|||
m_NavMeshLayer: 0 |
|||
m_StaticEditorFlags: 0 |
|||
m_IsActive: 1 |
|||
--- !u!114 &106586302 |
|||
MonoBehaviour: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 106586301} |
|||
m_Enabled: 1 |
|||
m_EditorHideFlags: 0 |
|||
m_Script: {fileID: 11500000, guid: c3d607733e457478885f15ee89725709, type: 3} |
|||
m_Name: |
|||
m_EditorClassIdentifier: |
|||
agentParameters: |
|||
maxStep: 5000 |
|||
hasUpgradedFromAgentParameters: 1 |
|||
maxStep: 5000 |
|||
--- !u!114 &106586303 |
|||
MonoBehaviour: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 106586301} |
|||
m_Enabled: 1 |
|||
m_EditorHideFlags: 0 |
|||
m_Script: {fileID: 11500000, guid: 5d1c4e0b1822b495aa52bc52839ecb30, type: 3} |
|||
m_Name: |
|||
m_EditorClassIdentifier: |
|||
m_BrainParameters: |
|||
vectorObservationSize: 1 |
|||
numStackedVectorObservations: 1 |
|||
vectorActionSize: 01000000 |
|||
vectorActionDescriptions: [] |
|||
vectorActionSpaceType: 0 |
|||
m_Model: {fileID: 0} |
|||
m_InferenceDevice: 0 |
|||
m_BehaviorType: 0 |
|||
m_BehaviorName: My Behavior |
|||
m_TeamID: 0 |
|||
m_UseChildSensors: 1 |
|||
--- !u!4 &106586304 |
|||
Transform: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 106586301} |
|||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} |
|||
m_LocalPosition: {x: 0, y: 0, z: 0} |
|||
m_LocalScale: {x: 1, y: 1, z: 1} |
|||
m_Children: |
|||
- {fileID: 1471486645} |
|||
m_Father: {fileID: 0} |
|||
m_RootOrder: 2 |
|||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} |
|||
--- !u!1 &185701317 |
|||
GameObject: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
serializedVersion: 6 |
|||
m_Component: |
|||
- component: {fileID: 185701319} |
|||
- component: {fileID: 185701318} |
|||
m_Layer: 0 |
|||
m_Name: Directional Light |
|||
m_TagString: Untagged |
|||
m_Icon: {fileID: 0} |
|||
m_NavMeshLayer: 0 |
|||
m_StaticEditorFlags: 0 |
|||
m_IsActive: 1 |
|||
--- !u!108 &185701318 |
|||
Light: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 185701317} |
|||
m_Enabled: 1 |
|||
serializedVersion: 8 |
|||
m_Type: 1 |
|||
m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} |
|||
m_Intensity: 1 |
|||
m_Range: 10 |
|||
m_SpotAngle: 30 |
|||
m_CookieSize: 10 |
|||
m_Shadows: |
|||
m_Type: 2 |
|||
m_Resolution: -1 |
|||
m_CustomResolution: -1 |
|||
m_Strength: 1 |
|||
m_Bias: 0.05 |
|||
m_NormalBias: 0.4 |
|||
m_NearPlane: 0.2 |
|||
m_Cookie: {fileID: 0} |
|||
m_DrawHalo: 0 |
|||
m_Flare: {fileID: 0} |
|||
m_RenderMode: 0 |
|||
m_CullingMask: |
|||
serializedVersion: 2 |
|||
m_Bits: 4294967295 |
|||
m_Lightmapping: 4 |
|||
m_LightShadowCasterMode: 0 |
|||
m_AreaSize: {x: 1, y: 1} |
|||
m_BounceIntensity: 1 |
|||
m_ColorTemperature: 6570 |
|||
m_UseColorTemperature: 0 |
|||
m_ShadowRadius: 0 |
|||
m_ShadowAngle: 0 |
|||
--- !u!4 &185701319 |
|||
Transform: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 185701317} |
|||
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} |
|||
m_LocalPosition: {x: 0, y: 3, z: 0} |
|||
m_LocalScale: {x: 1, y: 1, z: 1} |
|||
m_Children: [] |
|||
m_Father: {fileID: 0} |
|||
m_RootOrder: 1 |
|||
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} |
|||
--- !u!1 &804630118 |
|||
GameObject: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
serializedVersion: 6 |
|||
m_Component: |
|||
- component: {fileID: 804630121} |
|||
- component: {fileID: 804630120} |
|||
- component: {fileID: 804630119} |
|||
m_Layer: 0 |
|||
m_Name: Main Camera |
|||
m_TagString: MainCamera |
|||
m_Icon: {fileID: 0} |
|||
m_NavMeshLayer: 0 |
|||
m_StaticEditorFlags: 0 |
|||
m_IsActive: 1 |
|||
--- !u!81 &804630119 |
|||
AudioListener: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 804630118} |
|||
m_Enabled: 1 |
|||
--- !u!20 &804630120 |
|||
Camera: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 804630118} |
|||
m_Enabled: 1 |
|||
serializedVersion: 2 |
|||
m_ClearFlags: 1 |
|||
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} |
|||
m_projectionMatrixMode: 1 |
|||
m_SensorSize: {x: 36, y: 24} |
|||
m_LensShift: {x: 0, y: 0} |
|||
m_GateFitMode: 2 |
|||
m_FocalLength: 50 |
|||
m_NormalizedViewPortRect: |
|||
serializedVersion: 2 |
|||
x: 0 |
|||
y: 0 |
|||
width: 1 |
|||
height: 1 |
|||
near clip plane: 0.3 |
|||
far clip plane: 1000 |
|||
field of view: 60 |
|||
orthographic: 0 |
|||
orthographic size: 5 |
|||
m_Depth: -1 |
|||
m_CullingMask: |
|||
serializedVersion: 2 |
|||
m_Bits: 4294967295 |
|||
m_RenderingPath: -1 |
|||
m_TargetTexture: {fileID: 0} |
|||
m_TargetDisplay: 0 |
|||
m_TargetEye: 3 |
|||
m_HDR: 1 |
|||
m_AllowMSAA: 1 |
|||
m_AllowDynamicResolution: 0 |
|||
m_ForceIntoRT: 0 |
|||
m_OcclusionCulling: 1 |
|||
m_StereoConvergence: 10 |
|||
m_StereoSeparation: 0.022 |
|||
--- !u!4 &804630121 |
|||
Transform: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 804630118} |
|||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} |
|||
m_LocalPosition: {x: 0, y: 1, z: -10} |
|||
m_LocalScale: {x: 1, y: 1, z: 1} |
|||
m_Children: [] |
|||
m_Father: {fileID: 0} |
|||
m_RootOrder: 0 |
|||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} |
|||
--- !u!1 &1471486644 |
|||
GameObject: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
serializedVersion: 6 |
|||
m_Component: |
|||
- component: {fileID: 1471486645} |
|||
- component: {fileID: 1471486648} |
|||
- component: {fileID: 1471486647} |
|||
- component: {fileID: 1471486646} |
|||
m_Layer: 0 |
|||
m_Name: Cube |
|||
m_TagString: Untagged |
|||
m_Icon: {fileID: 0} |
|||
m_NavMeshLayer: 0 |
|||
m_StaticEditorFlags: 0 |
|||
m_IsActive: 1 |
|||
--- !u!4 &1471486645 |
|||
Transform: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 1471486644} |
|||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} |
|||
m_LocalPosition: {x: 0, y: 0, z: 0} |
|||
m_LocalScale: {x: 1, y: 1, z: 1} |
|||
m_Children: [] |
|||
m_Father: {fileID: 106586304} |
|||
m_RootOrder: 0 |
|||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} |
|||
--- !u!65 &1471486646 |
|||
BoxCollider: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 1471486644} |
|||
m_Material: {fileID: 0} |
|||
m_IsTrigger: 0 |
|||
m_Enabled: 1 |
|||
serializedVersion: 2 |
|||
m_Size: {x: 1, y: 1, z: 1} |
|||
m_Center: {x: 0, y: 0, z: 0} |
|||
--- !u!23 &1471486647 |
|||
MeshRenderer: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 1471486644} |
|||
m_Enabled: 1 |
|||
m_CastShadows: 1 |
|||
m_ReceiveShadows: 1 |
|||
m_DynamicOccludee: 1 |
|||
m_MotionVectors: 1 |
|||
m_LightProbeUsage: 1 |
|||
m_ReflectionProbeUsage: 1 |
|||
m_RenderingLayerMask: 1 |
|||
m_RendererPriority: 0 |
|||
m_Materials: |
|||
- {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} |
|||
m_StaticBatchInfo: |
|||
firstSubMesh: 0 |
|||
subMeshCount: 0 |
|||
m_StaticBatchRoot: {fileID: 0} |
|||
m_ProbeAnchor: {fileID: 0} |
|||
m_LightProbeVolumeOverride: {fileID: 0} |
|||
m_ScaleInLightmap: 1 |
|||
m_PreserveUVs: 0 |
|||
m_IgnoreNormalsForChartDetection: 0 |
|||
m_ImportantGI: 0 |
|||
m_StitchLightmapSeams: 0 |
|||
m_SelectedEditorRenderState: 3 |
|||
m_MinimumChartSize: 4 |
|||
m_AutoUVMaxDistance: 0.5 |
|||
m_AutoUVMaxAngle: 89 |
|||
m_LightmapParameters: {fileID: 0} |
|||
m_SortingLayerID: 0 |
|||
m_SortingLayer: 0 |
|||
m_SortingOrder: 0 |
|||
--- !u!33 &1471486648 |
|||
MeshFilter: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 1471486644} |
|||
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} |
|
|||
fileFormatVersion: 2 |
|||
guid: 60783bd849bd242eeb66243542762b23 |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
部分文件因为文件数量过多而无法显示
撰写
预览
正在加载...
取消
保存
Reference in new issue