2020年4月25日 星期六

AWS SDK Unity 2019

If you have read and followed the official AWS SDK webpage, but you can not make it work in Unity 2019. I could help you here.
First, the AWS SDK should work in Unity 2018. If it does not work, please check the awsconfig.xml and link.xml setup. You may put those files under Assets\Resources. Here is the example for awsconfig.xml and link.xml.

link.xml

<?xml version="1.0" encoding="utf-8"?> 
<aws correctForClockSkew="true" region="us-east-1"> 
<logging logTo="UnityLogger" logResponses="Always" logMetrics="true" logMetricsFormat="JSON" /> 
<s3 useSignatureVersion4="true" /> </aws>
You may need to change the "us-east-1" to the region that you are using.

awsconfig.xml

<linker>
<!-- if you are using AWSConfigs.HttpClient.UnityWebRequest option-->
<assembly fullname="UnityEngine">
 <type fullname="UnityEngine.Networking.UnityWebRequest" preserve="all" />
 <type fullname="UnityEngine.Networking.UploadHandlerRaw" preserve="all" />
 <type fullname="UnityEngine.Networking.UploadHandler" preserve="all" />
 <type fullname="UnityEngine.Networking.DownloadHandler" preserve="all" />
 <type fullname="UnityEngine.Networking.DownloadHandlerBuffer" preserve="all" />
</assembly>
<assembly fullname="mscorlib">
 <namespace fullname="System.Security.Cryptography" preserve="all"/>
</assembly>
<assembly fullname="System">
 <namespace fullname="System.Security.Cryptography" preserve="all"/>
</assembly>
<assembly fullname="AWSSDK.Core" preserve="all"/>
<assembly fullname="AWSSDK.CognitoIdentity" preserve="all"/>
<assembly fullname="AWSSDK.SecurityToken" preserve="all"/>
<assembly fullname="AWSSDK.DynamoDBv2" preserve="all"/>
</linker>
In this example I am using the DynamoDB service so I add the <assembly fullname="AWSSDK.DynamoDBv2" preserve="all"/> . You should add the service that you are going to use here.
This setting should work in Unity 2018 and Unity 2019 "Editor" and "iOS", BUT NOT the Android device.

How to fix the AWS SDK for Unity 2019

After you follow the official AWS SDK page steps (including import the official SDK) and setup the awsconfig.xml and link.xml.
  1. Found and open the AndroidManifest.xml file which is under the "Assets\Plugins\Android".
  2. Change the "com.unity3d.player.UnityPlayerNativeActivity" to "com.unity3d.player.UnityPlayerActivity"
  3. Download the fixed SDK here https://lhkmarcus.com/AWS_SDK_Unity2019.zip
  4. Unzip and replace all the file to the Assets\AWSSDK in your unity project
Now your project should be work in both iOS and Android.

Build your own AWS SDK for Unity

You may want to build your own SDK if you are using other versions of AWS SDK. First you may found the AWS SDK code here: https://github.com/aws/aws-sdk-net. Then you may just change the AndroidInterop.cs that in your project. The AndroidInterop.cs file is under "aws-sdk-net-master\sdk\src\Core\Amazon.Util\Internal_unity". The file may look like this:

/*
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 * 
 *  http://aws.amazon.com/apache2.0
 * 
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using UnityEngine;

namespace Amazon.Util.Internal
{
    /// <summary>
    /// This class is used to make Android Java calls as an alternative to using Android macro's
    /// The class uses reflection but doesnto cache the PropertyInfo and MethodInfo, 
    /// so it should be sparingly used so as not to impact performance.
    /// </summary>
    public class AndroidInterop
    {
        /// <summary>
        /// The API makes a call to a static java method on a class and returns a typed parameter
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="className"></param>
        /// <param name="methodName"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static T CallStaticJavaMethod<T>(string className, string methodName, params object[] parameters)
        {
            Type androidJavaClassType = InternalSDKUtils.GetTypeFromUnityEngine("AndroidJavaClass");
            if (androidJavaClassType != null)
            {
                var javaUnityHelper = Activator.CreateInstance(androidJavaClassType, className);
                var callStaticMethod = androidJavaClassType.GetMethods()
                  .Where(x => x.Name == "CallStatic")
                  .FirstOrDefault();
                if (callStaticMethod != null)
                {
                    return (T)callStaticMethod.Invoke(javaUnityHelper, new object[] { methodName, parameters });
                }
            }
            return default(T);
        }

        /// <summary>
        /// The API makes a call to a static java method on a class and returns an object of Type AndroidJavaObject
        /// </summary>
        /// <param name="className"></param>
        /// <param name="methodName"></param>
        /// <returns></returns>
        public static object GetJavaObjectStatically(string className, string methodName)
        {
            Type androidJavaClassType = InternalSDKUtils.GetTypeFromUnityEngine("AndroidJavaClass");
            Type androidJavaObjectType = InternalSDKUtils.GetTypeFromUnityEngine("AndroidJavaObject");
            if (androidJavaClassType != null)
            {
                var javaClass = Activator.CreateInstance(androidJavaClassType, className);
                var callStaticMethod = androidJavaClassType.GetMethods()
                    .Where(x => x.Name == "CallStatic")
                    .First(x => x.ContainsGenericParameters);

                var genericStaticMethod = callStaticMethod.MakeGenericMethod(androidJavaObjectType);

                return genericStaticMethod.Invoke(javaClass, new object[] { methodName, new object[] { } });
            }
            return null;
        }

        /// <summary>
        /// This API makes a call to a method on an Object by passing the specified parameters and returns a typed parameter
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="androidJavaObject"></param>
        /// <param name="methodName"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static T CallMethod<T>(object androidJavaObject, string methodName, params object[] parameters)
        {
            var method = androidJavaObject.GetType().GetMethods().Where(x => x.Name == "Call").First(x => x.ContainsGenericParameters);
            var genericMethod = method.MakeGenericMethod(typeof(T));
            return (T)genericMethod.Invoke(androidJavaObject, new object[] { methodName, parameters });
        }

        /// <summary>
        /// This API makes a call to a method on an Object by passing the specified parameters and returns an object of type AndroidJavaObject
        /// </summary>
        /// <param name="androidJavaObject"></param>
        /// <param name="methodName"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static object CallMethod(object androidJavaObject, string methodName, params object[] parameters)
        {
            Type androidJavaObjectType = InternalSDKUtils.GetTypeFromUnityEngine("AndroidJavaObject");
            var method = androidJavaObject.GetType().GetMethods()
                .Where(x => x.Name == "Call")
                .First(x => x.ContainsGenericParameters);
            var genericMethod = method.MakeGenericMethod(androidJavaObjectType);
            return genericMethod.Invoke(androidJavaObject, new object[] { methodName, parameters });
        }

        /// <summary>
        /// This API get a typed value from a static field 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="className"></param>
        /// <param name="methodName"></param>
        /// <returns></returns>
        public static T GetStaticJavaField<T>(string className, string methodName)
        {
            Type androidJavaClassType = InternalSDKUtils.GetTypeFromUnityEngine("AndroidJavaClass");
            if (androidJavaClassType != null)
            {
                var javaUnityHelper = Activator.CreateInstance(androidJavaClassType, className);
                var staticGetter = androidJavaClassType.GetMethod("GetStatic");
                var genericGetter = staticGetter.MakeGenericMethod(typeof(T));

                if (genericGetter != null)
                {
                    return (T)genericGetter.Invoke(javaUnityHelper, new object[] { methodName });
                }
            }

            return default(T);
        }

        /// <summary>
        /// This API returns a value of type AndroidJavaObject from a static field
        /// </summary>
        /// <param name="className"></param>
        /// <param name="methodName"></param>
        /// <returns></returns>
        public static object GetStaticJavaField(string className, string methodName)
        {
            Type androidJavaClassType = InternalSDKUtils.GetTypeFromUnityEngine("AndroidJavaClass");
            Type androidJavaObjectType = InternalSDKUtils.GetTypeFromUnityEngine("AndroidJavaObject");
            if (androidJavaClassType != null)
            {
                var javaUnityHelper = Activator.CreateInstance(androidJavaClassType, className);
                var staticGetter = androidJavaClassType.GetMethod("GetStatic");
                var genericGetter = staticGetter.MakeGenericMethod(androidJavaObjectType);

                if (genericGetter != null)
                {
                    return genericGetter.Invoke(javaUnityHelper, new object[] { methodName });
                }
            }
            return null;
        }

        /// <summary>
        /// This API returns a value of type AndroidJavaObject from a field on an android java object
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="androidJavaObject"></param>
        /// <param name="methodName"></param>
        /// <returns></returns>
        public static T GetJavaField<T>(object androidJavaObject, string methodName)
        {
            var method = androidJavaObject.GetType().GetMethods().Where(x => x.Name == "Get").First(x => x.ContainsGenericParameters);
            var genericMethod = method.MakeGenericMethod(typeof(T));
            return (T)genericMethod.Invoke(androidJavaObject, new object[] { methodName });
        }

        /// <summary>
        /// Returns an AndroidJavaObject 
        /// </summary>
        /// <returns></returns>
        public static object GetAndroidContext()
        {
            return GetStaticJavaField("com.unity3d.player.UnityPlayer", "currentActivity");
        }

    }
}

The problem is the androidJavaObject.GetType().GetMethods().Where(). After Unity 2019.1, It requires AndroidJavaObject[] but not the object[]. You may find the fixed AndroidInterop.cs here: https://lhkmarcus.com/post_src/AndroidInterop.cs

AWS SDK Unity 2019

Note


In most of the case, you just replace the AWSSDK.Core.dll would work. If your other dll (ex: AWSSDK.S3.dll, AWSSDK.DynamoDBv2.dll ) is too new/old that not match with the AWSSDK.Core.dll I uploaded, you may need to replace the other dll too.

2020年1月31日 星期五

建立自己的個人網頁


你是自顧人士或Indie Developer嗎?
我也是,請看看我的個人網頁:
https://lhkmarcus.com/


我只用了一天時間建立這個網頁
作為自顧人士需要以有限的時間達成可觀的效果
以下是我用來上存網頁和編輯網頁的工具
https://www.hostg.xyz/aff_c?offer_id=6&aff_id=28604

連結包含優惠碼,請在有效期間使用

2019年9月6日 星期五

香港註冊無限公司

上一篇文章,談到要不要開自己的公司/怎樣開公司,其實就算你不註冊公司,也可以運用不同平台來收錢,但正規來講,要做生意就需要有註冊公司,還有如果你不註冊公司,在App Store只可以用你自己的名稱在APP的開發者中顯示,不能自稱其他名稱如ABC Team,還有某些公司需要你有BR(商業登記)才提供服務給你,例如Paypal或者某些提位網頁的公司,所以這裡會講解一下如何註冊無限公司幫助你能做到基本的營運。

先說你要準備的費用,香港註冊公司費用每年也可能有改變,可以預港幣 250-2250 一年,詳細在這裡可以看到,以2019年4月後來說,現在註冊公司只需要港幣250,維期一年。

如果費用上沒有問題,就可以去註冊,你可能知道香港可以網上註冊公司,但條件是你要有電子證書(也要錢的),結果也是要出街,所以在這裡先說如何到稅務局公司。要注意的你如果你是獨資無限公司,不是去公司註冊處,而是去稅務局。

網上有申請表格的參考,不過你不能列印出來遞交,到稅務局就可以拿到表格,只需要帶身分證和費用去就可以,注意地址和辦公時間

填好表格到窗口交表,等待30分鐘左右叫號碼,付款後就可以拿到你的BR了!

2019年9月5日 星期四

香港工程師的業餘工作


有此行業很容易找到Freelance,例如設計網頁,卡片,Sales等。你是一位 Electrical Engineer (電子工程師) 或 Software Engineer (軟件工程師) 嗎?我是一位會寫Software的電子工程師,在香港我暫時沒看到有像外國一些Freelance平台方便找到job。

無平台就不如自己主動出擊,例如寫APP到App Store或Play Store賣吧!

但發現當你要踏出第一步的時候,就出現好多問題是香港才有,連Google也沒有答案😱

你可能會遇到的問題:

  1. 我要不要開自己的公司/怎樣開公司?
  2. 我需要開啟公司銀行才可以收到App Store或Play Store的錢嗎?
  3. 怎樣加廣告到APP?
  4. 需要開網頁嗎?
  5. 幫自己的作品打廣告要多少錢?
  6. 有很多公司稱幫助你開網頁電郵或打廣告,怎樣選擇?
  7. 香港可以用Google Domains嗎?
  8. 為什麼我在Google AdSense填表後把我彈回登入頁?

.....

可能還有很多問題,雖然工程師習慣Try and Error,不過無參考地試真花時間。日後我會在這個Blog講解一下怎樣處理這些問題 ,請留意這個Blog的更新啦~

2019年9月4日 星期三

An app for my son

When my son playing a train driving game:

Son: Dad I want to turn left.
Me: Son, the train does not work like this. It only runs along the railway.
Son: BUT I WANT TO TURN LEFT!
Me: Well, my be I can build one for you.


The idea is when you want to turn left, the left railway would be generated.
This app may release on Play Store and App Store later.

Nice to meet you!


I am Marcus who founded this company - LAMPSTAND TECHNOLOGY CO. I am a dad and an engineer. To be specific, I am an electrical engineer. However I built a lot of device that interact with smart phone.  I know how to program in C#, Objective C, Java, etc for Windows, iOS and android device. I would like to share my experience in this blog. 

For a parent, you may find some useful app here for your son or daughter.
For an engineer, you may find some useful information here about development.
For an entrepreneur, you may contact me for any technical advice.

我是在光科技公司的創辦人Marcus,我是一位爸爸,也是一位會寫軟件的電子工程師。我會寫APP在市場上售賣,也會寫APP給自己的兒女玩😄,我會在這個Blog分享我一些經驗。

如果你是位家長,你可能會在這裡找到適合你兒女的APP。
如果你是位工程師,你可能會在這裡找到關於你專業上的資訊。
如果你是位企業家,你可以聯絡我尋求一些技術上的建議。