Maxime FRAPPAT

Hum …no thanks ! – Lordinaire

Tag: WP8 (Page 1 of 2)

[Unity] Custom build for WP8/W8

If your game target the Windows Phone 8 or Windows Store platform, you probably want to use some specific feature like the share, live tiles, …

This can be done by using some tricks that I already explain in a previous thread (http://blog.lordinaire.fr/2014/09/unity-interact-between-unity-and-windows-phone-8-windows-store-apps/) but I might be pretty cool to build automatically custom files like splashscreen, MainPage or tiles.

Create a specific menu item

The first step is to create a menu item to easily access to our build.

using System.Linq;
using System.Security.Policy;
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System;

public class AutoBuilderMenu : MonoBehaviour
{
	[MenuItem("AutoBuilder/WP8")]
	private static void PerformBuild()
	{
		// TODO : Build and replace files
	}
}

For more information about MenuItem see http://blog.lordinaire.fr/2014/10/unity-create-custom-menu-items/

Simple build

using System.Linq;
using System.Security.Policy;
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System;

public class AutoBuilderMenu : MonoBehaviour
{
	[MenuItem("AutoBuilder/WP8")]
	private static void PerformBuild()
	{
		// Save current editor build target
		var currentTarget = EditorUserBuildSettings.activeBuildTarget;
		EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTarget.WP8Player);

		var buildPath = GetBuildPath(target);
		var path = Path.GetDirectoryName(buildPath);

		var folderExists = Directory.Exists(path);
		if (!folderExists)
			Directory.CreateDirectory(path);

		// Create solution folder and project files
		BuildPipeline.BuildPlayer(GetScenePaths(), buildPath, target, BuildOptions.None);

		// Set saved build target
		EditorUserBuildSettings.SwitchActiveBuildTarget(currentTarget);
	}

	private static string GetProjectName()
	{
		string[] s = Application.dataPath.Split('/');
		return s[s.Length - 2];
	}

	private static string[] GetScenePaths()
	{
		return EditorBuildSettings.scenes.Where(s => s.enabled).Select(s => s.path).ToArray();
	}

	private static string GetBuildPath(BuildTarget target)
	{
		var extension = String.Empty;

		return String.Format("../Builds/{0}/{1}/{2}/{3}",
			GetProjectName(),
			PlayerSettings.bundleVersion,
			target.ToString(),
			PlayerSettings.bundleIdentifier);
	}
}

The project will be create in the folder ../Builds/{ProjectName}/{BundleVersion}/{TargetPlatform}/{BundleIdentifier} so we have a build for each version of our game in a specific folder.

Custom build

using System.Linq;
using System.Security.Policy;
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System;

public class AutoBuilderMenu : MonoBehaviour
{
	[MenuItem("AutoBuilder/WP8")]
	private static void PerformBuild()
	{
		// Save current editor build target
		var currentTarget = EditorUserBuildSettings.activeBuildTarget;
		EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTarget.WP8Player);

		var buildPath = GetBuildPath(target);
		var path = Path.GetDirectoryName(buildPath);

		var folderExists = Directory.Exists(path);
		if (!folderExists)
			Directory.CreateDirectory(path);

		// First build to create solution folder and project files
		BuildPipeline.BuildPlayer(GetScenePaths(), buildPath, target, BuildOptions.None);

		// Replace custom files
		UpdateWP8Settings(buildPath);

		// Rebuild with replacing files
		BuildPipeline.BuildPlayer(GetScenePaths(), buildPath, target, BuildOptions.None);

		// Set saved editor build target
		EditorUserBuildSettings.SwitchActiveBuildTarget(currentTarget);
	}

	private static void UpdateWP8Settings(string buildPath)
	{
		// SplashScreenImage
		var replace = string.Concat(Application.dataPath, "/../BuildSettings/WP8/SplashScreenImage.jpg");
		var current = string.Format("{0}/{1}/SplashScreenImage.jpg", buildPath, PlayerSettings.bundleIdentifier);
		if (File.Exists(replace))
			File.Copy(replace, current, true);

		// MainPage.xaml.cs
		replace = string.Concat(Application.dataPath, "/../BuildSettings/WP8/MainPage.xaml.cs");
		current = string.Format("{0}/{1}/MainPage.xaml.cs", buildPath, PlayerSettings.bundleIdentifier);
		if (File.Exists(replace))
			File.Copy(replace, current, true);		

		// ApplicationIcon
		replace = string.Concat(Application.dataPath, "/../BuildSettings/WP8/ApplicationIcon.png");
		current = string.Format("{0}/{1}/Assets/ApplicationIcon.png", buildPath, PlayerSettings.bundleIdentifier);
		if (File.Exists(replace))
			File.Copy(replace, current, true);

		// WMAppManifest
		replace = string.Concat(Application.dataPath, "/../BuildSettings/WP8/WMAppManifest.xml");
		current = string.Format("{0}/{1}/Properties/WMAppManifest.xml", buildPath, PlayerSettings.bundleIdentifier);
		if (File.Exists(replace))
			File.Copy(replace, current, true);
	}

	private static string GetProjectName()
	{
		string[] s = Application.dataPath.Split('/');
		return s[s.Length - 2];
	}

	private static string[] GetScenePaths()
	{
		return EditorBuildSettings.scenes.Where(s => s.enabled).Select(s => s.path).ToArray();
	}

	private static string GetBuildPath(BuildTarget target)
	{
		return String.Format("../Builds/{0}/{1}/{2}/{3}",
			GetProjectName(),
			PlayerSettings.bundleVersion,
			target.ToString(),
			PlayerSettings.bundleIdentifier);
	}
}

The trick is to build twice the game. First we build to create all files then we replace our custom files and we rebuild to use these new files.

A more elegant way

private static void PerformBuild(BuildTarget target, bool isBeta = false)
{
	var currentTarget = EditorUserBuildSettings.activeBuildTarget;
	EditorUserBuildSettings.SwitchActiveBuildTarget(target);

	var buildPath = GetBuildPath(target);
	var path = Path.GetDirectoryName(buildPath);

	var folderExists = Directory.Exists(path);
	if (!folderExists)
	{
		Directory.CreateDirectory(path);
	}

	BuildPipeline.BuildPlayer(GetScenePaths(), buildPath, target, BuildOptions.None);

	if (target == BuildTarget.WP8Player)
	{
		UpdateWP8Settings(buildPath, string.Concat(Application.dataPath, "/../BuildSettings/WP8/"), string.Concat(buildPath, "/"));
		if (isBeta)
		{
			UpdateWP8Settings(buildPath, string.Concat(Application.dataPath, "/../BuildSettings/WP8_Beta/"), string.Concat(buildPath, "/"));

		}
		BuildPipeline.BuildPlayer(GetScenePaths(), buildPath, target, BuildOptions.None);
	}

	EditorUserBuildSettings.SwitchActiveBuildTarget(currentTarget);
}

private static void UpdateWP8Settings(string buildPath, string sourceRootPath, string destinationRootPath = "")
{
	var files = Directory.GetFiles(sourceRootPath);
	foreach (var file in files)
	{
		var replace = file;
		var fileName = replace.Split('/').LastOrDefault().Split('\\').LastOrDefault();
		var current = string.Concat(destinationRootPath, "/" + fileName);
		File.Copy(replace, current, true);
	}

	var directories = Directory.GetDirectories(sourceRootPath);
	foreach (var directory in directories)
	{
		var folderName = directory.Split('/').LastOrDefault().Split('\\').LastOrDefault();
		UpdateWP8Settings(buildPath, directory, destinationRootPath + "/" + folderName);
	}
}

You just need to add all your files in the folder BuildSettings/WP8 and/or BuildSettings/WP8_Beta with the same hierarchy as the project and it’s done ! Thanks Jonathan for the tips ;)

unity_custom_build

It’s the same way with Windows Store apps :)

[Unity][iOS/WP8/Android] Slide gesture helper

In this post, we will see how to detect a slide gesture in 2D mobile games.

Slide gesture description

In our example, a slide can be in four directions : left, right, bottom and top.

using Assets.Scripts.Framework;

namespace Assets.Scripts.Gestures
{
    public enum GestureType
    {
        Left,
        Top,
        Right,
        Bottom,
        Unknow
    }
}

We define a minimum distance and a ratio between vertical and horizontal distance to make the gesture completely.

left-slide-gesture

Gesture manager

using Assets.Scripts.Framework;
using Assets.Scripts.Gestures;
using UnityEngine;

namespace Assets.Scripts.Managers
{
    public enum HandlePatternResponseType
    {
        None,
        Error,
        Waiting,
        Ok
    }

    public class GestureManager : MonoBehaviour
    {
        public float MinDistanceForHorizontalSlide = 200;
        public float MinDistanceForVerticalSlide = 300;
        public float HorizontalRatio = 5;
        public float VerticalRatio = 5;

        private Vector3 _startPosition;
        private Vector3 _endPosition;

        private bool _hasStartPosition;

        /// <summary>
        /// Handle slide gesture
        /// </summary>
        public HandlePatternResponseType HandleSlideGesture(GestureType gestureType)
        {
            var result = HandlePatternResponseType.Waiting;

			// The finger is down
            if (Input.touchCount == 1)
            {
				// Save start position
                if (!_hasStartPosition)
                    _startPosition = TouchHelper.GetTouchedPosition();

                _endPosition = TouchHelper.GetTouchedPosition();
                _hasStartPosition = true;
            }

			// The finger is up
            if (Input.touchCount == 0 && _hasStartPosition)
            {
                _hasStartPosition = false;

                var distHorizontal = _endPosition.x - _startPosition.x;
                var distVertical = _endPosition.y - _startPosition.y;

                switch (gestureType)
                {
                    case GestureType.Left:
                        if ((distHorizontal * -1) < MinDistanceForHorizontalSlide
                            || (distHorizontal * -1) < (Mathf.Abs(distVertical) * HorizontalRatio))
                            result = HandlePatternResponseType.Error;
                        else
                            result = HandlePatternResponseType.Ok;
                        break;

                    case GestureType.Right:
                        if (distHorizontal < MinDistanceForHorizontalSlide
                            || distHorizontal < (Mathf.Abs(distVertical) * HorizontalRatio))
                            result = HandlePatternResponseType.Error;
                        else
                            result = HandlePatternResponseType.Ok;
                        break;

                    case GestureType.Top:
                        if (distVertical < MinDistanceForVerticalSlide
                            || distVertical < (Mathf.Abs(distHorizontal) * VerticalRatio))
                            result = HandlePatternResponseType.Error;
                        else
                            result = HandlePatternResponseType.Ok;
                        break;

                    case GestureType.Bottom:
                        if ((distVertical * -1) < MinDistanceForVerticalSlide
                            || (distVertical * -1) < (Mathf.Abs(distHorizontal) * VerticalRatio))
                            result = HandlePatternResponseType.Error;
                        else
                            result = HandlePatternResponseType.Ok;
                        break;

                    default:
						result = HandlePatternResponseType.None;
                        break;
                }
            }

            return result;
        }

    }
}

Example

  1. Create an empty game object and add it in the scene
  2. Create a new script : GameManager.cs
  3. Add this script in the new game object
public class GameManager : MonoBehaviour
{
	private GestureManager _gestureManager;

	public void Start()
	{
		_gestureManager = GetComponent<GestureManager>();
		if (_gestureManager == null)
		{
			_gestureManager = gameObject.AddComponent<GestureManager>();
		}
	}

	public void Update()
	{
		var result = _gestureManager.HandleSlideGesture(GestureType.Left);
		if (result == HandlePatternResponseType.Ok)
		{
			Debug.Log("[GESTURE] Left slide detected");
		}
		else if (result == HandlePatternResponseType.Error)
		{
			Debug.Log("[GESTURE] Bad gesture detected");
		}
	}
}

Have fun ! :)

Page 1 of 2

Powered by WordPress & Theme by Anders Norén

%d bloggers like this: