About Jinghui Dong

An engineer student from Cohort4 class in EAE program in University of Utah. Be passionate about the game development, game design, and still interested in art.

Android M Runtime Permission Issue in Unity Game Development

关于有史以来遇见过的最麻烦的游戏bug: Android M Permission

问题描述:

Android M 允许玩家在游戏安装后,修改游戏访问设备的权限。当玩家禁止游戏访问设备摄像头之后,游戏中的AR scene 依旧可以照常运行并不显示任何提示信息。

具体权限访问改变参考此处:https://blog.xamarin.com/requesting-runtime-permissions-in-android-marshmallow/

经过调查,游戏AR scene 的错误监控系统来自 Vufornia 的自带错误监控系统。然而,糟糕的是,Vufoirnia 并没有兼容 Android 的最新版本。每次AR scene 初始化时,访问的 Init 信息为 success。 这意味着,我必须自创一种检测方式,来检测游戏是否有权限访问设备的摄像头。

最开始,我尝试了两种方法:WebCamTexture (http://docs.unity3d.com/ScriptReference/WebCamTexture.html) 和 CameraDevice( https://developer.vuforia.com/library/sites/default/api/unity/classVuforia_1_1CameraDevice.html)。

前者是 Unity 提供的 api,后者是 Vuforinia 提供的对象。我本以为,如果摄像头访问权限受限后,api 返回的 active device 数目应该为 0。但是,实际返回对象数目依旧是 2 (前置和后置摄像头)。其次,我试图通过摄像头的各种参数,来区分权限访问情况。但是,无论是检测 Camera 的 buffer 是否在上一帧有更新,或者是检查 Camera 的 field of view,即使 permission 被禁止了,这些信息依旧可以被访问。

于是,我不得不把方向转向 Android M 的常规解决办法。http://jijiaxin89.com/2015/08/30/Android-s-Runtime-Permission/ 和 https://developer.android.com/training/permissions/requesting.html

这里的关键 api 是 checkSelfPermission。要调用该 api,最直接的办法是,创建自己的 jar,并添加到项目中。之后再用 C# 代码调用。但是,还有一种更简单的办法,即用 Unity 提供的 api 直接调用 Android 函数。方法在此:http://docs.unity3d.com/ScriptReference/AndroidJavaClass.html

我们既可以通过 AndrpidJavaClass 调用 static 函数,可以通过 AndroidJavaObject 调用 non-static 函数。以下是我的测试代码:

  1.             AndroidJavaClass jc = new AndroidJavaClass(“com.unity3d.player.UnityPlayer”);
  2.             AndroidJavaObject activity = jc.GetStatic<AndroidJavaObject>(“currentActivity”);
  3.             AndroidJavaObject context = activity.Call<AndroidJavaObject>(“getApplicationContext”);
  4.             AndroidJavaObject ContextCompat = new AndroidJavaObject(“android.support.v4.content.ContextCompat”);
  5.             AndroidJavaClass ActivityCompatClass = new AndroidJavaClass(“android.support.v4.app.ActivityCompat”);
  6.             int hasCameraPermission = context.Call<int>(“checkSelfPermission”, “android.permission.CAMERA” );
  7.             ActivityCompatClass.CallStatic(“requestPermissions”, context, “android.permission.CAMERA”, 1000 );
  8.             //int hasCameraPermission = ContextCompat.Call<int>(“checkSelfPermission”, context, “android.permission.CAMERA” );

但是糟糕的是,如果我用 ContextCompat.checkSelfPermission 调用 static 函数,程序将会报错显示无法找到该 static 函数。如果我使用 context.checkSelfPermission 调用 non-static 函数,虽然 method 会被调用,但是返回的值永远都是 0 (granted)。

后来经过再三检查,发现其问题在于我们使用的 library 不是最新版本,无法支持 Android api 23 及以上的版本。如果更新 lib 的话,会花费相当多的时间去清理和调整。

最后,我们尝试更改 Manifest 中的 targetVersion 来禁止用户修改游戏的访问权限。但是,这种方法也失败了。原因在于 targetVersion 的具体意思是指告知设备该程序已经在通过 targetVersion 的目标版本测试。然而我们的游戏实际并不满足目标版本,但是设备依然会按照应有的方式处理我们的程序。详细见此:

http://www.cnblogs.com/popapa/p/android_uses-sdk-element.html
http://developer.android.com/guide/topics/manifest/uses-sdk-element.html

最后是网上另外两名用户的解决办法: http://blog.trsquarelab.com/2015/11/  和 http://stackoverflow.com/questions/35027043/implementing-android-6-0-permissions-in-unity3d/

 

Unity: Gimbal Lock Again

I have to say, I have heard the word “gimbal lock” tons of time before. But recently a gimbal lock bug in Unity definitely reminded me of how naive I was about this problem. Actually, gimbal lock problem should not be seen in Unity. However, this is not always true.

What is gimbal lock?

Gimbal lock is the loss of one degree of freedom in a three-dimensional, three-gimbal mechanism that occurs when the axes of two of the three gimbals are driven into a parallel configuration, “locking” the system into rotation in a degenerate two-dimensional space.

This problem is frequently seen when we use euler angle for game object’s rotation, as one axis is set to 90 (in Unity axis order, this axis is x), then we lost one dimension. You could easily reproduce this problem in Unity Editor by set one game object’s rotation as (90, 0, 0). Then no matter how you change the value for y or z, the game object would only rotate in one dimension.

Detailed Information about Gimabl Lock

How Unity deals with euler?

Later, I checked the source code of Unity API. Here is what I found:

For transform.eulerAngles

public Vector3 eulerAngles
{
      get
      {
        return this.rotation.eulerAngles;
      }
      set
      {
        this.rotation = Quaternion.Euler(value);
      }
}

For transform.Rotate

public void Rotate(Vector3 eulerAngles, [DefaultValue("Space.Self")] Space relativeTo)
    {
      Quaternion quaternion = Quaternion.Euler(eulerAngles.x, eulerAngles.y, eulerAngles.z);
      if (relativeTo == Space.Self)
        this.localRotation = this.localRotation * quaternion;
      else
        this.rotation = this.rotation * Quaternion.Inverse(this.rotation) * quaternion * this.rotation;
    }

It is obvious that Unity internally uses quaternion instead of euler. In fact, euler is just the data showing to users. According to resources online, people believe gimbal lock could be solved by using quaternion. But now, I have doubt about it.

We could make a simple experiment like what two bloggers did here:

Candycat1992 made this test:

transform.rotation = Quaternion.Identity;
transform.Rotate(new Vector3(0, 0, 40));  
transform.Rotate(new Vector3(0, 90, 0));  
transform.Rotate(new Vector3(80, 0, 0)); 

And Scott did this:

void Start () { 
		     newDirection = new Quaternion();
		     newDirection  = Quaternion.Euler(-90, 45, 0);
     //newDirection  = Quaternion.Euler(-90, 0, 45);
	}
 
	void Update () { 
		     transform.rotation = Quaternion.Lerp(transform.rotation, newDirection, Time.deltaTime);
}

And gimbal lock appeared! But wait, how could quaternion fail to avoid gimbal lock?

Then I made my own test:

  • Set the rotation of the target object as (90, 0, 0)
  • Call transform.Rotate( 0, rate, 0 )
  • Call transform.Rotate( 0, 0, rate )

Gimbal lock never happens.

So what actually happened?

There are two concepts I misunderstood:

  1. Unity rotation order: In Unity, the order of rotation is ZXY. So if I set the original rotation of the object as (90, 0, 0), and then transform.Rotate(0, rate, 0) and transform.Rotate(0, 0, rate). It would not generate the gimbal lock. Because here it rotates in the order of XYZ, as rotation in X would not make any affect. But, if we call transform.Rotate( 90, y, z) directly, Unity would rotate in order of ZXY. In this way, it would generate gimbal lock.
  2. The way to use Quaternion: Quaternion could avoid gimbal lock, which is true. But remember, “could avoid” doesn’t mean always avoid. We could consider Quaternion as four dimension, comparing to three dimension of Euler, it has an extra dimension to avoid gimbal lock. However, if you rotate it the same as Euler axes, Quaternion does the same as Euler.  Therefore, we should be very careful about the way we use Quaternion.

Quaternion and gimbal lock is not easy to understand. So far I only know what went wrong, and I need to do more research about Quaternion and gimbal lock.

Unity UI adjustment based on different screen resolution

There are three ways which we could use to adjust UI elements based on different screen resolution:

  1. Surrounding fill out
  2. Relative size and fixed size
  3. Two patterns

Surrounding fill out

For surrounding fill out, it means that all game elements are placed in the center of the screen, and fill out rest of the screen space by using other elements, like merely black color. Here is the example from “Fallout:Shelter”:

16:9 ( for iphone5)

Screen Shot 2015-08-17 at 11.29.16 AM.png

4:3 ( for ipad)

Screen Shot 2015-08-17 at 11.28.19 AM.png

You can see, all UI elements are places in the center square. No matter what screen resolution it is, each UI element stays in the same position, and rest of space whether be cut (4:3) or filled in black (16:9). It is obviously a smart solution. Meanwhile, it presents a clean style for users. Personally I like this solution the most.

Pros:

  • Easy to implement
  • Clean style, looks perfect.

Cons:

  • All UI elements must be in the center
  • All UI elements’ size could not be adjusted

Summary

If there are a few interactive UI elements in the game scene, and each element’s size is not so small (like button), I suggest you use this method to solve UI adjustment issue.

Relative size and fixed size

For relative size and fixed size, UI element’s size and position are set as relative or fixed. Usually, the size is set as relative, and position is set as fixed. In Unity, we could use NGUI stretch and NGUI anchor to achieve it. In general, we want UI element’s size would be automatically adjusted according to screen size, and UI element’s position stays the same as resolution changes in order to make layout unchanged. Here is the example from Dead Trigger 2:

16:9 (iphone 5)

Screen Shot 2015-08-17 at 11.29.34 AM.png

4:3 ( ipad)

Screen Shot 2015-08-17 at 11.28.39 AM.png

It is clear that all buttons stay in the same position, as they use the same anchor target. If you look at green button on the right, you could notice that the distance between it and screen border is the same for 16:9 resolution and 4:3 resolution. Moreover, the iphone version has relatively bigger buttons as  we want player are able to tap them easily on iphone device. And it could be achieved by using NGUI stretch to set button’s relative size.

Pros:

  • Keep UI layout unchanged
  • UI elements would be not places in a certain place of the screen

Cons:

  • It might be generated unexpected result like buttons overlap with each other

Summary

If you want to have UI elements adjust their size based on device screen resolution, especially you want player on iphone could have relatively bigger UI elements, you could use this method. But you should be careful when elements are very close to each other. Because they might overlap with each other if their size keep increasing but they stay in the same position.

Two patterns

For two patterns, it is easy to understand. The game would use two different pattern of UI based on screen resolution. Here is the example from Hearthstone:

16:9 (iphone5)

Screen Shot 2015-08-17 at 11.28.57 AM.png

4 : 3 (ipad)

Screen Shot 2015-08-17 at 11.27.57 AM.png

Pros:

  • Provide the best interaction experience by choosing the appropriate pattern according to screen resolution

Cons:

  • You need to do everything twice

Summary

If you have enough resources and time, just do it!

Error Handler

Usually in game development, we want each error to be detected immediately and processed gracefully. Recently I am working on error handler to handle all different error states in our game. Here I would like to talk about it in detailed.

To begin with, it should be clear that error detection and error process should be separated. In language like C++ and Java, they use try-catch to handle the error. But using try-catch might not be always a good idea. Joel Spolsky pointed out two drawbacks of using try-catch.

  1. They are invisible in the source code. Looking at a block of code, including functions which may or may not throw exceptions, there is no way to see which exceptions might be thrown and from where. This means that even careful code inspection doesn’t reveal potential bugs.
  2. They create too many possible exit points for a function. To write correct code, you really have to think about every possible code path through your function. Every time you call a function that can raise an exception and don’t catch it on the spot, you create opportunities for surprise bugs caused by functions that terminated abruptly, leaving data in an inconsistent state, or other code paths that you didn’t think about.

Moreover, for me, I would like to have my own error message data structure. Therefore, rather than use try-catch directly, I create my own error handler class. The general idea of this class is still similar to try-catch, it has two main functionality, which are error detection and error handle.

Here is the workflow when an error occurs:

  1. One error occurs
  2. All listeners handle this error event. If the error is solved, clear the error.
  3. Check whether error still exist
  4. If yes, bing player back to the first scene
  5. Show error message to player (UI pop up)
  6. Player could check whether error is solved by pressing error message UI button.

Back to my ErrorHandler class, it has following things:

Function

  • void ErrorStateReport();                                             //Call back function when an error is detected
  • bool CheckErrorState( ErrorType i_error);            //Check one certain type of error whether exists.
  • bool CheckError();                                                       //Check whether there is still an error
  • void ClearError();                                                        //Clear error, set errorType to None.
  • void SetError( ErrorType i_error);                         // Set errorType to a certain type of error.

Data Member

  • readonly Dictionary<ErrorType, string> ErrorMessageDictionary;                // Store display information for each error type
  • ErrorType ErrorType;                                                                                                 //Error type.
  • delegate void OnErrorState( string errorMessage, ErrorType errorType);     //Error delegate
  • event OnErrorStateEvent OnErrorState;                                                                //Error event

Here is the workflow by using ErrorHandler class:

Scene where error occurs

  • //Error occurs
  • ErrorHandler.ErrorStateReport( Error );                                                       //All listener would handle the error
  • if(ErrorClientHandler.CheckError()) ErrorClientHandler.ErrorType = // one error type
  • //Back to first scene

First scene

  • if(ErrorClientHandler.CheckError())  ShowErrorUI( ErrorClientHandler.ErrorMessageDictionary[ ErrorClientHandler.ErrorType], ErrorClientHandler.ErrorType);
  • //….we are in error state now
  • //If player press error message UI button
  • if( !ErrorClientHandler.CheckErrorState(ErrorClientHandler.ErrorType) ) //error solved
  • //Keep retrying, error still exists.

This is ErrorHandler class I create. And it could match the requirement of our game’s error handle perfectly.

 

New Update

I have to say game reset is also a very dangerous activity. It is ironic that when I tried to use error handler to reset the game, it would bring new errors and crash the whole game.

This is what resetGame function did before:

  • Uninit the game;
  • Game back to main menu scene;

But when game uninited itself, some monobehaviour scripts were still running, and they might want to have the access to game data. Unfortunately, game data just destroyed itself. Then new error (null exception) came out, and at last game crashed.

The solution to this problem is before uniniting the game data, make game first enter an empty game scene to ensure no object still has reference to game data. Then we uninit game data, and last bring player back to main menu scene. So the new workflow turns out to be:

  • Go to empty scene;
  • Uninit the game;
  • Game back to main menu scene;

Exceptions

Exceptions DoSomething()

Exceptions vs. status returns

About Unity asset memory leak issue

Loading and unloading asset could cause memory problem if we are not able to handle it carefully. Here you could find the problem we might meet, the solution and the reason why such a problem happens.

Example Problem

Texture assets remain in memory after scene changes

Description

When player is in scene A, those character textures that he has checked would be loaded, which is normal. But when he moves to scene B, those textures still remain in memory. To see memory profiler, go to Window -> Profiler, click memory panel, then chose detailed option. Run the game, click “Take Sampler:Editor”, then we could see all asset loaded in memory.

Solution:

The thing we need to do is set texture to null when dynamic card is destroyed (onDestroy()). Then call Resources.UnloadUnusedAssets() when the scene ends or changes as we use Resources.Load() to get the 2D texture.

Detailed Explanation

Usually we want to use Resources.UnloadUnusedAssets() to unload asset loaded from resource.And we have to make sure there is no reference to the asset itself before calling Resources.UnloadUnusedAssets(). In the example, the game object should be destroyed when scene changes.

However, Destroy() function does not work in a proper way. In detailed,  a lot objects in Unity, to be more precisely, all that are inherited from UnityEngine.Object, consists of two parts: a managed object and a native code c++ class in the engine itself. In the managed environment we only “see” the managed object that is “linked” with the native part. This is where two totally different worlds collide. When you call destroy on the managed object, Unity will destroy the native code part of the object. But the managed part of the class is still there! Unity will flag the class internally as “destroyed”.

In short, Unity uses a “trick” to make the managed part “pretend” to be null when the native part is destroyed. In the example, the character texture in dynamic card (2D texture derived from object) has not been destroyed correctly. There is still a reference to the texture asset loaded from Resources and Resources.UnloadUnusedAssets() could not unload it.

Therefore, in order to destroy the object properly, we have to set the asset to null, like it mentioned above. In this way, there is no reference to the texture asset loaded from Resources. And Resources.UnloadUnusedAssets() could work correctly at last.

 

Atlas Memory Problem

We probably meet the problem that atlas still remains in memory after scene changes. This is not what we want to see because usually the size of atlas is big. In order to solve that,we might need to do following things:

  • Call UIDrawCall.ReleaseInactive() or ReleaseAll()
  • Then call UnloadUnusedAssets().

Also, if the scene we work on has the static object, make sure object set to null before it is destroyed (including the texture, material and etc). Usually if we see ManagedStaticReference in “Reference by”, then we have to be careful with that.

Moreover, think twice before working on NGUI object, as NGUI object would have the reference to atlas. If the object is not destroyed correctly when scene changes, it could cause atlas remain in memory. 

Currently even Unity community could not provide available solution to atlas unloaded problem. And it is possible that NGUI or Unity itself still has some bugs. We have to rely on ourselves now.

 

Summary

To sum up, we need to check following issues if we meet similar memory problem:

1.Keep track of the asset loaded from resource, make sure it have been set to null when it is destroyed.

2.Be careful about the assignment operator. For example:

     Object obj = Resources.Load(“MyPrefab”);
GameObject instance = Instantiate(obj) as GameObject;
………
Destroy(instance);

      We also have to destroy obj if we want to call Resources.UnloadUnusedAssets() to unload the “MyPrefab” asset. This is why in 1 says “keep track of the loaded asset”. 

3.Make sure Resources.UnloadUnusedAssets() is called. Even though this function should be automatically called when scene changes ( I have tested it and it is true). It is better call Resources.UnloadUnusedAssets() explicitly just in case.

4.Make sure static object has been cleared up.

5. If the asset still could not be unloaded, try calling System.GC.Collect(). Although it might solve the problem, it is not recommended.

 

Reference

Does Destroy remove all instances?

Resources.UnloadUnusedAssets

Similar Texture Memory Problem

Unload NGUI atlas

All is Dust Thesis Game – Week_13_Work : All is Dust Item Description System Problem Fixed

Before we published the final version of our game, me and Huali found some severe bugs in our game. As we played the game by controller, the instruction in game still shows the keyboard input information (Like F) rather than controller input information ( Like X).

Then I checked the code for each game objects’s item description script, and I found out that some scripts have not been merged into the newest description system, making them more static instead of dynamic. In general, each object should share only one item description script, and the relative showing text could be found through a static description text script. But now, some game objects have their own description script, and showing text is set by the script itself, which is extremely inconvenient.

So I removed the unnecessary description script and merge those game objects into the item description system.  And now the game objects could show the text correctly according to the input type, as controller would show the information like “X”, and keyboard would show the information like “F”.

All is Dust Thesis Game – Week_12_Work : All is Dust Optimization

This week I took advantage of static batching to decrease the draw call in order to optimize the game.

Static Batching

Static batching, on the other hand, allows the engine to reduce draw calls for geometry of any size (provided it does not move and shares the same material). Static batching is significantly more efficient than dynamic batching. You should choose static batching as it will require less CPU power.

In order to take advantage of static batching, you need explicitly specify that certain objects are static and will not move, rotate or scale in the game. To do so, you can mark objects as static using the Static checkbox in the Inspector:

Using static batching will require additional memory for storing the combined geometry. If several objects shared the same geometry before static batching, then a copy of geometry will be created for each object, either in the Editor or at runtime. This might not always be a good idea – sometimes you will have to sacrifice rendering performance by avoiding static batching for some objects to keep a smaller memory footprint. For example, marking trees as static in a dense forest level can have serious memory impact.

Static batching does not reduce batches instead of draw calls. Their number stays the same, but they are a lot faster with static batching.

After I set the static game objects as static batching, about 600 draw calls have been removed.

All is Dust Thesis Game – Week_11_Work : All is Dust Bug Fixed

This week I have fixed several bugs in our game:

1.Remove duplicated terrain

I found that there are two terrains in night level three, making the game running kind of slowly. After removing the duplicated terrain, the game run much faster.

2.Dust obstacle bug

As player gets into the dust obstacle, the dust intensity would increase greatly. But the boundary of the dust obstacle has not been considered quite well, so sometimes the dust intensity would decrease wrongly. So I changed the shape of the dust obstacle’s boundary to circle, which solves this problem quite well.

3.Pot destroyed Text bug

After the pots have been destroyed, there should be no showing text anymore. And I made the program remove the floating name objects after the pots are destroyed, which would no longer confused players.

All is Dust Thesis Game – Week_9_10_Work : All is Dust Design 3.0

All is Dust Design Document Five

Now each point place should have more events and more information, which the event could be scarecrow attack, item picking up, puzzle and narrative elements.

The point places include: barn, animal pen, silo, windmill, well, wagon.

Player could explore any open point places at each night. He could choose the place he wants to go. Once he completes a certain place event, the map will show that place has been checked. Also, inside the house, we will use six candles to show how many events the player has experienced. Like this:

One event complete

candle

Three events complete

candle2

Even though we have six point places right now, we need to make sure the priority of these places. And these six places are divided into three groups:

Group 1: Silo, Wagon, Animal Pen

For these three places, player could explore since the beginning.

Group 2: Barn, Well

For these two places, player could not explore at night one. But after night one, he is able to explore them. Here, “could not explore” means this place would not show more information, or the event trigger would not be activated. For example, barn is locked at night one. So player could not explore the barn at first.

Group 3: Windmill

This is the final place of the game, so only at the last night could player explore this place.

In general, except group one, group two and group three places would be somehow “locked” at night one. And group two would be activated at night two, and group three would be activated at final night.

 

1.Animal Pen

Goal: Player burn the bone in the pit.

Event: After player burn the bone, more fire would rise on the ground and form the shape of evil symbol.

Player enters the animal pen.

  1. Finds the pit with bone, notice that all animals were dead.
  2. Get the gasoline.
  3. Pull it into the pit and then burn it.
  4. The fire rise, forming in the shape of devil symbol.

“The animal pen has been abandoned so long. I remember we were planning to burn these bodies before we left. But then… Patricia? Damn it! I still could not remember what happened later.”

2.Wagon

Goal: Find the wheels of the wagon.

Event: After player finds the two missing wheels and put them back to the wagon, these wheels will rotate, with noise kind of like moan.

  1. Player finds the broken wagon.
  2. Get the information that two wheels are missing.
  3. Find two missing wheels in the surrounding place.
  4. Put them back to the wagon.
  5. Hear the noise as wheels begin to rotate.
  6. When player leaves the wagon, scarecrow appears and then chase the player.

“Kill you all… Kill… Kill…All of you…”

3.Silo

Goal: Reach the top of the silo and collect the lost pages

Event: Scarecrow will be activated once player reaches the silo. If player stays on the top of the silo too long, crows will attack player.

And more trees should be added to silo places, because we don’t want player could see silo clearly from far distance. Something should be hided first.

A test in Unity3D.

silo

4.Well

Goal: Get the item in the bucket

Event: Find the crank and then get the key in the bucket.

5.Barn

No change.

6.Windmill

No change.

All is Dust Thesis Game – Week_8_Work: All is Dust Player 3.0 Feedback

Problem of the Gameplay:

Gameplay & Emotion

  1. The tension of the whole gameplay is too continuous, player only could have a breath when he is inside the house or on the top of the silo. In this way, the emotion response would not be as strong as when he has at the beginning of the game.
  2. Player likes reaching the top of the silo. This place make him feel safe. So he tends to stay there for a while. We might take advantage of it to provide more information to player at that moment. Or once player gets down, he will be busy running and does not have time to check some information.
  3. The key in the well could not be noticed by some players. In GDC booth, they were trying picking up the item again and again even though the key had been already been picked up.
  4. Daughter’s ghost should react a little slower than player. Like when player chases her, after one second, she would run. In this way, player would feel he might be able to reach her, making him has the desire to chase her.
  5. The instruction is too obvious, and player feels like he just needs to follow the instruction and go to point A and point B. Player wants to explore by themselves. We could provide goal for him to explore, but he does not like we lead him to explore.
  6. The car crash at the beginning does not affect the gameplay later on. In this way, player does not feel it is the car crash brings him to this unrealistic place. And player even does not feel he would die. We should provide some feedback to player about the car crash.
  7. Ghost should build the connection between player and his daughter. Because

Scarecrow

  1. The logic of scarecrow is still confusing. Even though player knows scarecrow would disappear, but he could figure it out why scarecrow behaves like this. Let’s put it in another way, if a vampire disappears when you use the lantern to shed light on it, player would not get confused. But right now in our game, scarecrow’s behavior could not be understood clearly.
  2. The way of scarecrow attacking is somehow confuses the player. Our scarecrow is neither a strong monster nor creepy ghost. Once player finds that scarecrow could not kill you immediately, he will give up looking back to scarecrow. Instead, player would keep running.
  3. When player looks at scarecrow from a far distance, the scarecrow should not stay still. Player does not like walking backward just to avoid the attack from scarecrow.
  4. Once player gets hit, he still has much freedom to escape. In detailed, player could run to anywhere even though scarecrow attacks him. Player does not lose so much after getting hit. Then he would not afraid of scarecrow so much.

Farm & House

  1. The layout of the house should be adjusted. Most of players could not find Patricia’s room is locked at first because there is a table on the right side of player when game begins. Player would not go across the table and then check the door. Also, the model of Patricia’s room does not look like an inside room door. Some players thought that door was to the outside. Meanwhile, the backdoor should be nailed with plank. Then player would get the idea that this door could not be opened.
  2. The backroom inside the house should be used on purpose. Many players explore the backroom several times but find out nothing happen there. Everything in game should make sense somehow, or it should be removed.
  3. The path of night three should be changed as player get lost easily. Also after the pot are destroyed, the text should be not shown anymore.
  4. Player could not notice he is in a farm at first night. The connection between player and farm has not been built through the whole gameplay. We should focus on how to build the connection between player and farm.
  5. The corn seems to be creepy at first, and player expects some events in the corn. But then he finds nothing happens in corn field. And corn does not mean anything special anymore.
  6. Newspaper and family photo should be added to the game.

 

Problem of the Test itself:

  1. We should provide more comfortable environment for the testers. For example, we should provide chairs for players. Only in a comfortable and safe place, then the player could engage totally into the game. So in the future, we should pay attention to this.
  2. We should encourage players to speak out their feelings when they are playing our game, like what they see, what they are doing, what cause confusing. For example, in the car scene, player might say something like: “I am driving a car, I don’t know where to go. Wait, something shining out there, what is that? The car is damn broken, and I don’t want to go outside.” From player’s words, we could find something we have notices before or even player has not noticed by himself.
  3. Make a list of questions and ask them to players one by one after they finish the test. The questions should be easy and detailed at first, like “Do you know Patricia’s room is locked?” Then the questions should be more abstract and focus on big picture, like “Could you describe the game story based on your understanding.” In this way, player could speak out what they think and how they feel about our game.