Skip to content

Home

將Unity 3D遊戲提交到Mac應用商店

經過三個月的週末開發,我們的Unity 3D遊戲已經準備好發布並部署到App Store。然而,這個過程並不直觀。我花了一整夜來解決它。經過許多嘗試和錯誤,我決定在這裡記錄一些關鍵的步驟:

![./2016-05-29.png]

1. Unity生成設置

在Unity中,齁到"檔案" > "建立設置" > "平台: PC, Mac與Linux獨立" > "目標平台: Mac OS"。

點擊"玩家設置...",並配置以下選項:

默認為全屏=真
默認是原生分辨率=真
抓取單一螢幕=假
顯示分辨率對話框=假
Mac應用商店驗證=真
2. Info.plist

在Finder中,右擊遊戲應用並選擇'顯示包內容'。在內容文件夾中,編輯Info.plist文件。確保:

  1. CFBundleGetInfoString是一個有效的字符串。
  2. CFBundleIdentifier和CFBundleSignature的值與bundle id(稍後解釋)匹配。
  3. CFBundleShortVersionString和CFBundleVersion的格式為x.x.x,例如1.0.0。
  4. 新增一個<key>LSApplicationCategoryType</key>,值為<string>public.app-category.games</string>

以下面的例子為例:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
     <!-- ... your keys and values here ... -->
    </dict>
    </plist>
3. 授權資訊

在你的Build文件夾中,創建一個名為GAMENAME.entitlements的文件,添加一個sandbox鍵,如下所示:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>com.apple.security.app-sandbox</key>
        <true/>
    </dict>
    </plist>
4. Apple開發者帳戶

假設你已經有一個付費的Apple開發者帳戶,訪問 Apple Developer Account。然後,轉到證書並選擇"OS X"的下拉列表。點擊"+"按鈕創建新的證書。你需要完成兩次這個過程以獲取"Mac應用分發"和"Mac安裝程序分發"證書。將它們保存到你的鑰匙鏈以供日後使用。

然後,轉到"識別碼"並選擇"應用ID"標籤。創建一個萬用應用ID,但要確保它與前一步驟中的bundle ID值匹配。例如,我的是unity.victorleungtw.*

5. iTunes Connect

登錄到 iTunes Connect,轉到 我的應用 > "+" > "新的Mac應用",並填寫所需的欄位。確定 Bundle ID 與前一階段的配置相符。前綴欄位應為遊戲名稱,例如在我的情況下,它是ufo

你也需要拍攝屏幕截圖,並將它們修剪到正確的大小。只允許以下的尺寸:

  • 1280 x 800 像素
  • 1440 x 900 像素
  • 2560 x 1600 像素
  • 2880 x 1800 像素
6. 應用加載器

下載並安裝最新的應用加載器,用來提交應用。完成以下步驟:

在終端機中填寫內容權限:

    chmod -R a+xr "/path/to/GAMENAME.app"

用第三步中創建的授權信息文件簽署應用:

    codesign -f -s '3rd Party Mac Developer Application: DEVELOPER NAME' --entitlements "GAMENAME.entitlements" "/AppPath/GAMENAME.app"

你可能會遇到以下的錯誤:

    code object is not signed at all

這是因為子元件沒有簽署。你可以逐個簽署它們,包括libmono.0.dyliblibMonoPosixHelper.dylib,但個更簡單的方法是使用深度簽名命令,如下所示:

    codesign -f -s '3rd Party Mac Developer Application: DEVELOPER NAME' --entitlements "GAMENAME.entitlements

" "/AppPath/GAMENAME.app" --deep

然後,構建.pkg文件:

    productbuild --component GAMENAME.app /Applications --sign "3rd Party Mac Developer Installer: DEVELOPER NAME" GAMENAME.pkg

從你的機器中刪除所有已存在的遊戲應用。然後你可以用下面的命令驗證原則:

    sudo installer -store -pkg GAMENAME.pkg -target /

最後,打開應用加載器,選擇'提交你的應用',並選擇GAMENAME.pkg。如果一切正常,上傳應該會成功。否則,根據相應的錯誤來解決問題。這個過程需要一些時間。

7. 選擇構建

如果你收到了包含如下問題的郵件:

> **無效簽名** — 主應用包game在路徑GAMENAME.app有以下簽名錯誤:在架構i386中的Info.plist無效(plist或簽名已被修改)。

這可能是你的某個子元件沒有正確簽署。你可能需要參考蘋果的文件尋找進一步的指導。

否則,返回到iTunes Connect,選擇已上傳的構建,並將狀態從"準備審查"變更為"等待審查" > "審查" > "銷售準備就緒"。

我希望這個博客文章對你有所幫助,也希望可以為你節省一些時間!:)

Resolving Merge Conflicts for the Unity Game Engine

I'm working with a team of four on a Unity 3D game project over the weekends. It's a lot of fun, but we've encountered problems with version control. Using Git and GitHub, we've faced many merge conflicts that are not easy to resolve; it's not as simple as just deleting a section or performing a forced push:

    <<<<<<< HEAD:main.scene
    Painful
    =======
    Delete me
    >>>>>>>

There are lots of unnecessary local meta files that get pushed to our repository. The solution I've found is not perfect, but it works:

First, open the Unity editor and go to:

    Edit -> Project Settings -> Editor ->
    Select "**Visible Meta files**" in the version control mode

Second, add a .gitignore file like this:

    /[Ll]ibrary/
    /[Tt]emp/
    /[Oo]bj/
    /[Bb]uild/
    /[Bb]uilds/
    /Assets/AssetStoreTools*

    # Autogenerated VS/MD solution and project files
    ExportedObj/
    *.csproj
    *.unityproj
    *.sln
    *.suo
    *.tmp
    *.user
    *.userprefs
    *.pidb
    *.booproj
    *.svd

    # Unity3D generated meta files
    *.pidb.meta

    # Unity3D Generated File On Crash Reports
    sysinfo.txt

    # Builds
    *.apk
    *.unitypackage

    .DS_Store

Then, commit the actual changes and run the following commands:

    git rm -r --cached .
    git add .
    git commit -m "Fixed untracked files"

Third, Unity has a tool called UnityYAMLMerge for merging scene and prefab files. Enable this by creating a .gitconfig file with the following:

    [merge]
    tool = unityyamlmerge

    [mergetool "unityyamlmerge"]
    trustExitCode = false
    cmd = /Applications/Unity/Unity.app/Contents/Tools/UnityYAMLMerge merge -p "$BASE" "$REMOTE" "$LOCAL" "$MERGED"

The next time a teammate clones the project, they may initially see an empty scene. However, there's no need to panic. Simply open the saved main.scene (assuming you have saved the scene and committed it), and everything else should work as expected. I wish Unity had built-in source control like other IDE environments. Happy coding!

解決Unity遊戲引擎的合併衝突

我正和一個四人的團隊在周末進行一個Unity 3D遊戲項目的工作。這很有趣,但我們遇到了版本控制的問題。使用Git和GitHub,我們遇到了許多不容易解決的合併衝突; 它不僅僅是刪除一個部分或執行強制推送那麼簡單:

    <<<<<<< HEAD:main.scene
    痛苦
    =======
    刪除我
    >>>>>>>

有很多不必要的本地元資料檔案被推送到我們的存儲庫。我找到的解決方案並不完美,但它有效:

首先,打開Unity編輯器並前往:

    編輯 -> 項目設置 -> 編輯器 ->
    在版本控制模式中選擇 "**可見Meta文件**"

其次,添加一個 .gitignore 資料檔案像這樣:

    /[Ll]ibrary/
    /[Tt]emp/
    /[Oo]bj/
    /[Bb]uild/
    /[Bb]uilds/
    /Assets/AssetStoreTools*

    # 自動生成的VS/MD解決方案和項目文件
    ExportedObj/
    *.csproj
    *.unityproj
    *.sln
    *.suo
    *.tmp
    *.user
    *.userprefs
    *.pidb
    *.booproj
    *.svd

    # Unity3D生成的meta文件
    *.pidb.meta

    # Unity3D在崩潰報告上生成的文件
    sysinfo.txt

    # Builds
    *.apk
    *.unitypackage

    .DS_Store

然後,提交實際的更改並運行以下命令:

    git rm -r --cached .
    git add .
    git commit -m "Fixed untracked files"

第三,Unity有一個叫UnityYAMLMerge的工具用於合併場景和prefab文件。通過創建一個.gitconfig文件並使用以下內容來啟用:

    [merge]
    tool = unityyamlmerge

    [mergetool "unityyamlmerge"]
    trustExitCode = false
    cmd = /Applications/Unity/Unity.app/Contents/Tools/UnityYAMLMerge merge -p "$BASE" "$REMOTE" "$LOCAL" "$MERGED"

下次隊伍的某個成員克隆該項目時,他們可能最初會看到一個空的場景。但是,不必慌張。只需打開已保存的main.scene(假設您已經保存並提交了場景),其他東西就應該如預期的那樣工作。我希望Unity像其他IDE環境一樣內置源代碼控制。編碼愉快!

Trivial Facts About JavaScript Date

Fact 1

The integer value representing the month in JavaScript begins with 0 for January and goes up to 11 for December. For example:

Today's date can be represented as:

> var date = new Date(2016, 3, 29, 11, 10, 30)

instead of:

> var date = new Date(2016, 4, 29, 11, 10, 30)
Fact 2

In JavaScript, the date is based on a time value expressed in milliseconds, whereas the UNIX timestamp is expressed in seconds since 00:00:00 UTC on January 1, 1970.

For example, to convert a UNIX timestamp to a JavaScript date:

> var date = new Date(UNIX_Timestamp * 1000);

關於JavaScript日期的瑣碎事實

事實1

在JavaScript中,代表月份的整數值由0開始表示1月,至11表示12月。例如:

今天的日期可以表示為:

> var date = new Date(2016, 3, 29, 11, 10, 30)

而不是:

> var date = new Date(2016, 4, 29, 11, 10, 30)
事實2

在JavaScript中,日期是基於以毫秒表示的時間值,而UNIX時間戳則是從1970年1月1日00:00:00 UTC開始以秒為單位表示。

例如,要將UNIX時間戳轉換為JavaScriptdate:

> var date = new Date(UNIX_Timestamp * 1000);

It's Not a Tech Problem, Stupid

I enjoy helping people and solving complex problems. However, it's disheartening to encounter issues that fall outside the scope of a programmer's ability to solve them. If you lack an engineering background, you may believe that your challenges are technological in nature—that a website, an app, or a machine learning server might be the solution. But in reality, these are rarely the root causes. Here are what I consider to be your seven most critical issues:

  1. You're Not Delivering on Your Promises: What is the goal of your product? What problem are you trying to solve? Do you understand your customers' needs? How does your product stand out in the market? Do you have control over the delivery process? If not, you might be wasting time and resources building something that nobody wants.

  2. You're Risk-Averse: You fear making significant changes because something might break. Whether it's messy CSS files, deprecated browsers, or spaghetti code that no one wants to touch, these issues demand attention. It's crucial to clean up your tech debt so you can move forward.

  3. You Lack Creativity: Minor aesthetic problems, such as misaligned text or an off-center box, might seem easy to fix but can still detract from the overall design. Consider trusting your designer to make bold visual choices, or hire a user experience specialist to conduct A/B testing. You might also need to invest in high-quality content for SEO.

  4. You Form Superficial and Dishonest Relationships: Your lack of open communication obscures progress and roadblocks. You might be misleading investors with over-promises, creating an illusion that everything is fine. This lack of transparency will ultimately hurt everyone involved.

  5. You Foster Rivalry Within Your Team: Do you know why your team is unhappy? Is there an excessive workload causing burnout? Do they enjoy their work? Is there trust in the system? If the work environment is neither pleasant nor stimulating, it might be time to reconsider your leadership.

  6. You're Less Productive Than You Think: Do you notice a drop in productivity? Unfocused meetings without clear outcomes and trying to multitask without setting priorities not only waste your time but also the time of those around you.

  7. You're Not Humble: You prioritize being right over doing what's right. Because of your seniority and experience, you believe your viewpoint must be correct. Your ego clouds your judgment and prevents you from making objective decisions.

It's easy to identify flaws in software, but it's much harder to spot them in corporate culture. Addressing these fundamental issues should be your first step.

這不是技術問題,笨蛋

我喜歡幫助人並解決複雜的問題。但是,碰到超出程序員能力範圍解決的問題,我感到沮喪。如果你缺乏工程背景,你可能認為你的挑戰是技術上的 —— 你可能認為一個網站,一個應用程序,或者一個機器學習服務器可能是解決方案。但實際上,這些很少是根本原因。以下是我認為你的七大核心問題:

  1. 你沒有兌現你的承諾: 你的產品的目標是什麼?你試圖解決什麼問題?你了解你的客戶需求嗎?你的產品在市場上是如何嶄露頭角的?你對交付過程有控制權嗎?若否,你可能在浪費時間和資源製作無人需要的東西。

  2. 你害怕冒險: 你害怕進行重大改變,因為可能會出問題。無論是混亂的CSS文件,已經過時的瀏覽器,或者沒有人敢碰的紊亂程式碼,這些問題都需要關注。整理你的技術債務以便你能夠前進,這是至關重要的。

  3. 你缺乏創造力: 輕微的美觀問題,如文字對齊不齊或框框不在中央,可能看起來容易修復,但仍然會影響整體設計。嘗試信任你的設計師讓他們做出大膽的視覺選擇,或者雇用一個用戶體驗專家進行A/B 測試。你可能還需要投資於SEO的高質量內容。

  4. 你建立膚淺而不誠實的關係: 你缺乏開放的溝通,這使進展和障礙都模糊了。你可能用過度承諾來誤導投資者,制造一種一切都好的假象。這種不透明將最終傷害到所有人。

  5. 你孵化了你的團隊內的競爭: 你知道你的團隊為何不快樂嗎?是否有過多的工作量導致疲憊?他們喜歡他們的工作嗎?他們對系統有信心嗎?如果工作環境既不愉快也不刺激,這可能是時候重新考慮你的領導方式了。

  6. 你比你想象中的生產力低: 你有注意到生產力下降了嗎?沒有明確結果的無聚焦會議,以及試圖一心多用而又沒有設定優先順序,不僅浪費你的時間,也浪費了你周遭人的時間。

  7. 你不淡泊: 你優先考慮的是自己是對的,而非做出正確的事。因為你的資歷和經驗,你認為你的觀點一定是正確的。你的自我心理影響了你的判斷,阻止你做出客觀的決定。

我們很容易找出軟體中的錯誤,但在公司文化中發現錯誤則難上加難。解決這些基本問題應該是你的第一步。

What I Learned from Building Large-Scale Applications for Overseas Clients

The IT industry in China is rapidly growing. Over the last year in Hong Kong, I've had the privilege of working on an exciting project that has become a significant milestone in my career, offering me the opportunity to learn something new and innovative.

While working on many projects, it's generally more productive to have every team member located in the same place. However, I found myself collaborating with remote colleagues across various time zones, including Pacific Standard Time, UTC-06:00, and UTC+10:00, which wasn't ideal for productivity.

Another challenge is the language barrier with clients. For those who speak Chinese but aren't proficient typists, I recommend learning the pinyin input method to keep up with brief conversations in QQ Chat.

To the surprise of many foreigners, China censors GitHub, making it impossible to simply use npm install & bower install within the Great Firewall. Additionally, hosting on AWS and some analytics tools are blocked, necessitating the search for alternatives. Moreover, your developer colleagues may prefer a different work style, rather than adopting scrum methodology or using the JIRA board for project visibility.

Our public beta test of the Ember application encountered performance issues. The initial load and rendering times were slow, despite Ember's developer-friendly tools. Compounding the problem, at least 20% of our customers used low-end computers with outdated browsers. I was amazed to see Windows XP crash simply from loading font icons. We eventually addressed this issue by refactoring the application with vanilla JavaScript. Although this improved speed, it doubled the amount of code and tripled the number of states and bugs to manage, which was far from ideal.

One crucial lesson we learned the hard way is the importance of maintaining complete control over the delivery pipeline; otherwise, a release becomes unattainable. Shipping early and often is more of an art than a science. Despite working for months on three redesigns and receiving positive feedback, we were still unable to launch the project. The inertia inherent in large corporations and a culture resistant to change proved too difficult to overcome.

Through this project, we gained valuable insights into both the technical and project management aspects. Shipping early and often is best practice, and I look forward to applying these lessons to future projects.

我從為海外客戶構建大規模應用程序中學到的東西

中國的IT行業正在迅速增長。在過去的一年中,我在香港有幸參與了一個引人入勝的項目,這項目已經成為我職業生涯中的一個重要里程碑,給我提供了學習新事物和創新的機會。

雖然在許多項目中工作,但每個團隊成員都位於同一地點通常更具生產力。然而,我發現自己與不同時區的遠程同事合作,包括太平洋標準時間,UTC-06:00和UTC+10:00,這對生產力並不理想。

另一個挑戰是與客戶的語言障礙。對於會說中文但打字不熟練的人,我建議學習拼音輸入法以跟上QQ聊天中的簡短對話。

許多外國人驚訝地發現,中國審查了GitHub,使得在長城防火牆內簡單地使用npm install& bower install變得不可能。此外,AWS的主機以及一些分析工具也被封鎖,需要尋找替代方案。而且,你的開發同事可能更喜歡不同的工作方式,而不是採用scrum方法或是用JIRA板來提供項目的可見性。

我們的Ember應用程序公開測試遇到了性能問題。儘管Ember的開發工具非常友好,但初始加載和渲染時間仍然很慢。加劇這個問題的是,我們至少有20%的客戶使用過時的瀏覽器和低端電腦。令我驚訝的是,僅僅從加載圖標字體就讓Windows XP崩潰。最終,我們通過用原生JavaScript重構了應用程序來解決這個問題。雖然這提高了速度,但卻使代碼的數量加倍,並使要管理的狀態和錯誤的數量增加了三倍,這與理想情況相去甚遠。

我們用痛苦的方式學到了一個關鍵的教訓,那就是保持對交付管道的完全控制的重要性;否則,一個發布就變得難以達成。早發布,常發布,更多的是一種藝術而非科學。儘管我們花了幾個月的時間進行了三次重新設計並得到了積極的反饋,我們仍然無法啟動該項目。大公司內部的慣性和抵制改變的文化證明了這一點太難以克服。

通過這個項目,我們對技術和項目管理方面都獲得了寶貴的見解。早發布,常發布是最好的做法,我期待將這些經驗應用到未來的項目中。

How to Use Webpack with React and Bootstrap

Today, I was setting up a project using Webpack, React, and Bootstrap without jQuery. What seemed like a straightforward task ended up taking more time than expected, so I've decided to document the steps below:

Step 1: Install All Dependencies

First, install all the required dependencies:

npm install react react-dom bootstrap react-bootstrap babel-preset-react --save
npm install webpack css-loader style-loader file-loader url-loader babel-core babel-loader babel-preset-es2015 --save-dev

Step 2: Add Loaders in webpack.config.js

Next, add the necessary loaders to your webpack.config.js file:

var path = require("path")
var webpack = require("webpack")

module.exports = {
  entry: "./main.js",
  output: { path: __dirname, filename: "bundle.js" },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        loader: "babel-loader",
        exclude: /node_modules/,
        options: {
          presets: ["es2015", "react"],
        },
      },
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.png$/,
        loader: "url-loader?limit=100000",
      },
      {
        test: /\.jpg$/,
        loader: "file-loader",
      },
      {
        test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
        loader: "url-loader?limit=10000&mimetype=application/font-woff",
      },
      {
        test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
        loader: "url-loader?limit=10000&mimetype=application/octet-stream",
      },
      {
        test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
        loader: "file-loader",
      },
      {
        test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
        loader: "url-loader?limit=10000&mimetype=image/svg+xml",
      },
    ],
  },
}

Step 3: Import Bootstrap CSS in main.js

In your main.js file (or whatever your entry file is), import the Bootstrap CSS:

import App from "./app.jsx"
import Bootstrap from "bootstrap/dist/css/bootstrap.css"

Step 4: Import React Bootstrap in app.jsx

Finally, in your app.jsx, import React Bootstrap components:

import React from "react"
import ReactDOM from "react-dom"
import { Button } from "react-bootstrap"

const buttonsInstance = <Button>Click me!</Button>

ReactDOM.render(buttonsInstance, document.getElementById("here"))

HTML Setup

Don't forget to include a div with the appropriate ID, as well as the bundle.js:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello React</title>
  </head>
  <body>
    <div id="here"></div>
    <script src="bundle.js"></script>
  </body>
</html>

That's it! Feel free to reach out if you have any questions.