Skip to content
This repository has been archived by the owner on Dec 18, 2023. It is now read-only.

Commit

Permalink
Merge branch 'release/v0.4.0' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
rYuuk committed Jul 3, 2023
2 parents e06e9ce + 0c9b830 commit 9bf46d2
Show file tree
Hide file tree
Showing 23 changed files with 336 additions and 123 deletions.
13 changes: 0 additions & 13 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,6 @@
<!-- Fill the section below with Added, Updated and Removed information. -->
<!-- If there is no item under one of the lists remove it's title. -->

## Changes

#### Added

- List your additions and new features here.

#### Updated

- List your updates and changes here.

#### Removed

- List what is removed here.

<!-- Testability -->

Expand Down
47 changes: 47 additions & 0 deletions Documentation~/CustomizationGuide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Customization Guide

This guide will help you to customize the Ready Player Me avatar creator to fit your needs. The UI was designed in a way that separates the logic from the UI elements

## Requirements
You are allowed to change the entire UI, the only thing that you are required to have in your custom implementation is the Ready Player Me sign-in button and Ready Player Me account-creation UI. This is a legal requirement from Ready Player Me.

![image](https://github.com/readyplayerme/rpm-unity-sdk-avatar-creator/assets/1121080/c80e9bfa-a635-4ed4-878a-be9506d7d7c1)

![image](https://github.com/readyplayerme/rpm-unity-sdk-avatar-creator/assets/1121080/c2d8135b-f8e5-4c9d-9ea6-ca00f4af3514)


## Creating a Custom UI From Scratch

All required APIs are provided in the package. They can be used to create a new custom UI. The package also contains a sample UI that can be used as a reference. The following are the most important APIs that you will required.
- AuthManager - Requests for handling authentication.
- AvatarManager - Requests for creating, updating and deleting avatars.
- PartnerAssetManager - Requests for loading partner assets.

## Customizing the Sample

The sample comes with a default UI similar to the Web avatar creator. The UI is built using uGUI. All major UI components such as screen, asset buttons, asset type panels are prefabs, and can be edited easily. For detailed description of structure of the sample please see the [Sample Structure.](SampleStructure.md)

### Changing background color
- Select different screens under UI > AvatarCreatorCanvas > Creator > Screens and set color in image component or change the sprite.
- Select camera and change the background color.
- Select header under UI > AvatarCreatorCanvas > Creator and change the color.
- Select LoadingManager and change color for either type of loading screen.

Following demonstrate on how to change the background color of different screens.

https://github.com/readyplayerme/rpm-unity-sdk-avatar-creator/assets/1121080/ae412932-1bd9-4d00-b6b5-6525adecf9c7

### Adapting UI according to portrait mode
- Select a panel prefab from the "Prefabs/Panels" folder that you want to modify.
- Adjust the size and position of the panel to suit your desired location.
- To switch from a vertical to a horizontal layout, make the following changes:
- Locate the Grid Layout Group component and adjust the constraint from "Fixed Column Count" to "Fixed Row Count".
- Find the Scroll Rect component of the panel and select the "Horizontal" option while deselecting the "Vertical" option.

Following demonstrate on how to change avatar creation selection panels according to portrait mode. This is done in runtime but can be replicated by changing the prefabs as mentioned above.

https://github.com/readyplayerme/rpm-unity-sdk-avatar-creator/assets/1121080/f706e33d-8fb8-4226-8d3c-3e5b6bb17026




3 changes: 3 additions & 0 deletions Documentation~/CustomizationGuide.md.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ The plugin is currently in **alpha** stage. We recommend not to use it in the pr
- The package contains APIs required for creating, customizing and loading the avatar.
- It also contains a sample which demonstrate the usage of the APIs and replicates RPM web avatar creator.
- The documentation of provided sample can be found [here.](Documentation~/SampleStructure.md)

## Customization

The documentation for customization can be found [here.](Documentation~/CustomizationGuide.md)

## Note

- Camera support is currently only available for PC using Unity’s webcam native API.
Expand Down
30 changes: 30 additions & 0 deletions Runtime/AvatarManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,36 @@ public AvatarManager(BodyType bodyType, OutfitGender gender, AvatarConfig avatar
inCreatorAvatarLoader = new InCreatorAvatarLoader();
avatarAPIRequests = new AvatarAPIRequests(ctxSource.Token);
}

/// <summary>
/// Create a new avatar from a provided template.
/// </summary>
/// <param name="avatarProperties">Properties which describes avatar</param>
/// <returns>Avatar gameObject</returns>
public async Task<AvatarProperties> CreateFromTemplateAvatar(AvatarProperties avatarProperties)
{
try
{
avatarProperties = await avatarAPIRequests.CreateFromTemplateAvatar(
avatarProperties.Id,
avatarProperties.Partner,
bodyType
);
avatarId = avatarProperties.Id;
}
catch (Exception e)
{
OnError?.Invoke(e.Message);
return avatarProperties;
}

if (ctxSource.IsCancellationRequested)
{
return avatarProperties;
}

return avatarProperties;
}

/// <summary>
/// Create a new avatar.
Expand Down
10 changes: 9 additions & 1 deletion Runtime/Data/PartnerAssets.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Newtonsoft.Json;
using System.Collections.Generic;
using Newtonsoft.Json;
using ReadyPlayerMe.AvatarLoader;

namespace ReadyPlayerMe.AvatarCreator
Expand All @@ -12,5 +13,12 @@ public struct PartnerAsset
public OutfitGender Gender;
public string Icon;
public string Mask;
public LockedCategories[] LockedCategories;
}

public struct LockedCategories
{
public string Name;
public KeyValuePair<string, string>[] CustomizationCategories;
}
}
13 changes: 13 additions & 0 deletions Runtime/Data/TemplateData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Newtonsoft.Json;
using ReadyPlayerMe.AvatarLoader;

namespace ReadyPlayerMe.AvatarCreator
{
public struct TemplateData
{
public string ImageUrl;
[JsonConverter(typeof(GenderConverter))]
public OutfitGender Gender;
public string Id;
}
}
3 changes: 3 additions & 0 deletions Runtime/Data/TemplateData.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions Runtime/Data/UserSession.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json;

namespace ReadyPlayerMe.AvatarCreator
{
[Serializable]
public struct UserSession
{
[JsonProperty("_id")]
Expand Down
4 changes: 4 additions & 0 deletions Runtime/JsonHelpers/PartnerAssetsDictionaryConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ public override Dictionary<AssetType, object> ReadJson(JsonReader reader, Type o
}

var pascalCaseKey = char.ToUpperInvariant(element.Key[0]) + element.Key.Substring(1);
if(!Enum.IsDefined(typeof(AssetType), pascalCaseKey))
{
continue;
}
var assetType = (AssetType) Enum.Parse(typeof(AssetType), pascalCaseKey);
assets.Add(assetType, element.Value);
}
Expand Down
6 changes: 6 additions & 0 deletions Runtime/PartnerAssetsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ public async void DownloadAssetsIcon(Action<Dictionary<string, Texture>> onDownl
}
}

public bool IsLockedAssetCategories(string id)
{
var asset = assets.FirstOrDefault(x => x.Id == id);
return asset.LockedCategories != null && asset.LockedCategories.Length > 0;
}

private async Task<Dictionary<string, Texture>> DownloadIcons(List<PartnerAsset> chunk)
{
var assetIconMap = new Dictionary<string, Task<Texture>>();
Expand Down
76 changes: 69 additions & 7 deletions Runtime/WebRequests/AvatarAPIRequests.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using ReadyPlayerMe.AvatarLoader;
using ReadyPlayerMe.Core;
using UnityEngine;
using UnityEngine.Networking;

namespace ReadyPlayerMe.AvatarCreator
{
Expand All @@ -16,6 +18,12 @@ public class AvatarAPIRequests
private const string COLOR_PARAMETERS = "colors?type=skin,beard,hair,eyebrow";
private const string FETCH_AVATAR_PARAMETERS = "?select=id,partner&userId=";
private const string DRAFT_PARAMETER = "draft";
private const string TEMPLATE = "templates";
private const string FULL_BODY = "fullbody";
private const string HALF_BODY = "halfbody";
private const string PARTNER = "partner";
private const string DATA = "data";
private const string ID = "id";

private readonly AuthorizedRequest authorizedRequest;
private readonly CancellationToken ctx;
Expand All @@ -26,7 +34,7 @@ public AvatarAPIRequests(CancellationToken ctx = default)
authorizedRequest = new AuthorizedRequest();
}

public async Task<Dictionary<string, string>> FetchUserAvatars(string userId)
public async Task<Dictionary<string, string>> GetUserAvatars(string userId)
{
var response = await authorizedRequest.SendRequest<Response>(
new RequestData
Expand All @@ -39,8 +47,62 @@ public async Task<Dictionary<string, string>> FetchUserAvatars(string userId)
response.ThrowIfError();

var json = JObject.Parse(response.Text);
var data = json["data"]!;
return data.ToDictionary(element => element["id"]!.ToString(), element => element["partner"]!.ToString());
var data = json[DATA]!;
return data.ToDictionary(element => element[ID]!.ToString(), element => element[PARTNER]!.ToString());
}

public async Task<TemplateData[]> GetTemplates()
{
var response = await authorizedRequest.SendRequest<Response>(
new RequestData
{
Url = $"{Endpoints.AVATAR_API_V2}/{TEMPLATE}",
Method = HttpMethod.GET,
},
ctx: ctx
);
response.ThrowIfError();

var json = JObject.Parse(response.Text);
var data = json[DATA]!;
return JsonConvert.DeserializeObject<TemplateData[]>(data.ToString());
}

public async Task<Texture> GetTemplateAvatarImage(string url)
{
var downloadHandler = new DownloadHandlerTexture();
var webRequestDispatcher = new WebRequestDispatcher();
var response = await webRequestDispatcher.SendRequest<ResponseTexture>(url, HttpMethod.GET, downloadHandler: downloadHandler, ctx: ctx);

response.ThrowIfError();
return response.Texture;
}

public async Task<AvatarProperties> CreateFromTemplateAvatar(string avatarId, string partner, BodyType bodyType)
{
var payloadData = new Dictionary<string, string>
{
{ nameof(partner), partner },
{ nameof(bodyType), bodyType == BodyType.FullBody ? FULL_BODY : HALF_BODY },
};

var payload = AuthDataConverter.CreatePayload(payloadData);

var response = await authorizedRequest.SendRequest<Response>(
new RequestData
{
Url = $"{Endpoints.AVATAR_API_V2}/templates/{avatarId}",
Method = HttpMethod.POST,
Payload = payload
},
ctx: ctx
);

response.ThrowIfError();

var json = JObject.Parse(response.Text);
var data = json[DATA]!.ToString();
return JsonConvert.DeserializeObject<AvatarProperties>(data);
}

public async Task<ColorPalette[]> GetAllAvatarColors(string avatarId)
Expand Down Expand Up @@ -72,7 +134,7 @@ public async Task<AvatarProperties> GetAvatarMetadata(string avatarId)
response.ThrowIfError();

var json = JObject.Parse(response.Text);
var data = json["data"]!.ToString();
var data = json[DATA]!.ToString();
return JsonConvert.DeserializeObject<AvatarProperties>(data);
}

Expand All @@ -90,7 +152,7 @@ public async Task<AvatarProperties> CreateNewAvatar(AvatarProperties avatarPrope
response.ThrowIfError();

var metadata = JObject.Parse(response.Text);
var data = metadata["data"]!.ToString();
var data = metadata[DATA]!.ToString();
return JsonConvert.DeserializeObject<AvatarProperties>(data);
}

Expand Down
8 changes: 0 additions & 8 deletions Samples~.meta

This file was deleted.

6 changes: 3 additions & 3 deletions Samples~/Prefabs/DefaultAvatarButton.prefab
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: -50}
m_SizeDelta: {x: 400, y: 400}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 200, y: 200}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &3862883088835331388
CanvasRenderer:
Expand Down Expand Up @@ -108,7 +108,7 @@ RectTransform:
- {fileID: 546109656371019473}
- {fileID: 8294350063510174813}
m_Father: {fileID: 0}
m_RootOrder: 0
m_RootOrder: -1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
Expand Down
1 change: 1 addition & 0 deletions Samples~/Scripts/Data/AvatarCreatorData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace ReadyPlayerMe
public class AvatarCreatorData : ScriptableObject
{
public AvatarProperties AvatarProperties;
public bool IsExistingAvatar;

public void Awake()
{
Expand Down
Loading

0 comments on commit 9bf46d2

Please sign in to comment.