Kinect causing framerate limitation in Unity3D - c#

I'm currently developing a Unity3D project which uses both Oculus Rift and Kinect. However, Kinect is limiting the framerate to 30 fps and the Rift needs 60 fps to achieve a fluent experience. I'm using the official It's definitely not a performance issue.
I'm using this wrapper with the official Kinect SDK.
I've narrowed the cause down to this piece of code bellow. I'm suspecting that the getSkeleton() function is blocking the main thread until it has recieved data from the Kinect. Since the Kinect only runs at 30 fps the rest of the application cannot run faster than that.
public bool pollSkeleton () {
if (!updatedSkeleton)
{
updatedSkeleton = true;
if (kinect.pollSkeleton())
{
newSkeleton = true;
System.Int64 cur = kinect.getSkeleton().liTimeStamp;
System.Int64 diff = cur - ticks;
ticks = cur;
deltaTime = diff / (float)1000;
processSkeleton();
}
}
return newSkeleton;
}
Perhaps I could run a seperate thread, but since I have no experience with multithreaded programming I was wondering if anyone had a more simple solution?
My guess is that a lot of Oculus Rift developers will be using the Kinect+Unity3D combo and thus run into the same limitation as me. Any help will be greatly appreciated!
EDIT:
I created a seperate thread for polling the skeleton Info from the kinect: Here's the complete code for the modified SkeletonWrapper.cs. Hope it will help some of you guys struggling with the same issue.
using UnityEngine;
using System.Collections;
using Kinect;
using System.Threading;
public class SkeletonWrapper : MonoBehaviour {
public DeviceOrEmulator devOrEmu;
private Kinect.KinectInterface kinect;
private bool updatedSkeleton = false;
private bool newSkeleton = false;
[HideInInspector]
public Kinect.NuiSkeletonTrackingState[] players;
[HideInInspector]
public int[] trackedPlayers;
[HideInInspector]
public Vector3[,] bonePos;
[HideInInspector]
public Vector3[,] rawBonePos;
[HideInInspector]
public Vector3[,] boneVel;
[HideInInspector]
public Quaternion[,] boneLocalOrientation;
[HideInInspector]
public Quaternion[,] boneAbsoluteOrientation;
public Kinect.NuiSkeletonPositionTrackingState[,] boneState;
private System.Int64 ticks;
private float deltaTime;
private Matrix4x4 kinectToWorld;
public Matrix4x4 flipMatrix;
private Thread thread = null;
private bool isThreadRunning = false;
// Use this for initialization
void Start () {
kinect = devOrEmu.getKinect();
players = new Kinect.NuiSkeletonTrackingState[Kinect.Constants.NuiSkeletonCount];
trackedPlayers = new int[Kinect.Constants.NuiSkeletonMaxTracked];
trackedPlayers[0] = -1;
trackedPlayers[1] = -1;
bonePos = new Vector3[2,(int)Kinect.NuiSkeletonPositionIndex.Count];
rawBonePos = new Vector3[2,(int)Kinect.NuiSkeletonPositionIndex.Count];
boneVel = new Vector3[2,(int)Kinect.NuiSkeletonPositionIndex.Count];
boneState = new Kinect.NuiSkeletonPositionTrackingState[2,(int)Kinect.NuiSkeletonPositionIndex.Count];
boneLocalOrientation = new Quaternion[2, (int)Kinect.NuiSkeletonPositionIndex.Count];
boneAbsoluteOrientation = new Quaternion[2, (int)Kinect.NuiSkeletonPositionIndex.Count];
//create the transform matrix that converts from kinect-space to world-space
Matrix4x4 trans = new Matrix4x4();
trans.SetTRS( new Vector3(-kinect.getKinectCenter().x,
kinect.getSensorHeight()-kinect.getKinectCenter().y,
-kinect.getKinectCenter().z),
Quaternion.identity, Vector3.one );
Matrix4x4 rot = new Matrix4x4();
Quaternion quat = new Quaternion();
double theta = Mathf.Atan((kinect.getLookAt().y+kinect.getKinectCenter().y-kinect.getSensorHeight()) / (kinect.getLookAt().z + kinect.getKinectCenter().z));
float kinectAngle = (float)(theta * (180 / Mathf.PI));
quat.eulerAngles = new Vector3(-kinectAngle, 0, 0);
rot.SetTRS( Vector3.zero, quat, Vector3.one);
//final transform matrix offsets the rotation of the kinect, then translates to a new center
kinectToWorld = flipMatrix*trans*rot;
thread = new Thread(ThreadUpdate);
thread.Start();
}
void OnDestroy()
{
if (isThreadRunning)
{
isThreadRunning = false;
thread.Abort();
thread = null;
}
}
// Update is called once per frame
void Update () {
}
void LateUpdate () {
updatedSkeleton = false;
newSkeleton = false;
}
private void ThreadUpdate()
{
isThreadRunning = true;
while (isThreadRunning)
{
// This function is capping the FPS to 30.
if (kinect.pollSkeleton())
{
System.Int64 cur = kinect.getSkeleton().liTimeStamp;
System.Int64 diff = cur - ticks;
ticks = cur;
deltaTime = diff / (float)1000;
processSkeleton();
newSkeleton = true;
}
}
}
/// <summary>
/// First call per frame checks if there is a new skeleton frame and updates,
/// returns true if there is new data
/// Subsequent calls do nothing have the same return as the first call.
/// </summary>
/// <returns>
/// A <see cref="System.Boolean"/>
/// </returns>
public bool pollSkeleton () {
//if (!updatedSkeleton)
//{
// updatedSkeleton = true;
// //this function is capping the FPS to 30.
// //It might be blocking the main thread because it waits for the kinects skeleton input which only runs 30 fps
// //possible solution: run function in seperate thread
// if (kinect.pollSkeleton())
// {
// newSkeleton = true;
// System.Int64 cur = kinect.getSkeleton().liTimeStamp;
// System.Int64 diff = cur - ticks;
// ticks = cur;
// deltaTime = diff / (float)1000;
// processSkeleton();
// }
//}
return newSkeleton;
}
private void processSkeleton () {
int[] tracked = new int[Kinect.Constants.NuiSkeletonMaxTracked];
tracked[0] = -1;
tracked[1] = -1;
int trackedCount = 0;
//update players
for (int ii = 0; ii < Kinect.Constants.NuiSkeletonCount; ii++)
{
players[ii] = kinect.getSkeleton().SkeletonData[ii].eTrackingState;
if (players[ii] == Kinect.NuiSkeletonTrackingState.SkeletonTracked)
{
tracked[trackedCount] = ii;
trackedCount++;
}
}
//this should really use trackingID instead of index, but for now this is fine
switch (trackedCount)
{
case 0:
trackedPlayers[0] = -1;
trackedPlayers[1] = -1;
break;
case 1:
//last frame there were no players: assign new player to p1
if (trackedPlayers[0] < 0 && trackedPlayers[1] < 0)
trackedPlayers[0] = tracked[0];
//last frame there was one player, keep that player in the same spot
else if (trackedPlayers[0] < 0)
trackedPlayers[1] = tracked[0];
else if (trackedPlayers[1] < 0)
trackedPlayers[0] = tracked[0];
//there were two players, keep the one with the same index (if possible)
else
{
if (tracked[0] == trackedPlayers[0])
trackedPlayers[1] = -1;
else if (tracked[0] == trackedPlayers[1])
trackedPlayers[0] = -1;
else
{
trackedPlayers[0] = tracked[0];
trackedPlayers[1] = -1;
}
}
break;
case 2:
//last frame there were no players: assign new players to p1 and p2
if (trackedPlayers[0] < 0 && trackedPlayers[1] < 0)
{
trackedPlayers[0] = tracked[0];
trackedPlayers[1] = tracked[1];
}
//last frame there was one player, keep that player in the same spot
else if (trackedPlayers[0] < 0)
{
if (trackedPlayers[1] == tracked[0])
trackedPlayers[0] = tracked[1];
else{
trackedPlayers[0] = tracked[0];
trackedPlayers[1] = tracked[1];
}
}
else if (trackedPlayers[1] < 0)
{
if (trackedPlayers[0] == tracked[1])
trackedPlayers[1] = tracked[0];
else{
trackedPlayers[0] = tracked[0];
trackedPlayers[1] = tracked[1];
}
}
//there were two players, keep the one with the same index (if possible)
else
{
if (trackedPlayers[0] == tracked[1] || trackedPlayers[1] == tracked[0])
{
trackedPlayers[0] = tracked[1];
trackedPlayers[1] = tracked[0];
}
else
{
trackedPlayers[0] = tracked[0];
trackedPlayers[1] = tracked[1];
}
}
break;
}
//update the bone positions, velocities, and tracking states)
for (int player = 0; player < 2; player++)
{
//print(player + ", " +trackedPlayers[player]);
if (trackedPlayers[player] >= 0)
{
for (int bone = 0; bone < (int)Kinect.NuiSkeletonPositionIndex.Count; bone++)
{
Vector3 oldpos = bonePos[player,bone];
bonePos[player,bone] = kinectToWorld.MultiplyPoint3x4(kinect.getSkeleton().SkeletonData[trackedPlayers[player]].SkeletonPositions[bone]);
//bonePos[player,bone] = kinectToWorld.MultiplyPoint3x4(bonePos[player, bone]);
rawBonePos[player, bone] = kinect.getSkeleton().SkeletonData[trackedPlayers[player]].SkeletonPositions[bone];
Kinect.NuiSkeletonBoneOrientation[] or = kinect.getBoneOrientations(kinect.getSkeleton().SkeletonData[trackedPlayers[player]]);
boneLocalOrientation[player,bone] = or[bone].hierarchicalRotation.rotationQuaternion.GetQuaternion();
boneAbsoluteOrientation[player,bone] = or[bone].absoluteRotation.rotationQuaternion.GetQuaternion();
//print("index " + bone + ", start" + (int)or[bone].startJoint + ", end" + (int)or[bone].endJoint);
boneVel[player,bone] = (bonePos[player,bone] - oldpos) / deltaTime;
boneState[player,bone] = kinect.getSkeleton().SkeletonData[trackedPlayers[player]].eSkeletonPositionTrackingState[bone];
//print(kinect.getSkeleton().SkeletonData[player].Position.z);
}
}
}
}
}

I don't know much about how the Kinect SDK works. It's possible that it's already managing a background thread that is doing the skeletal tracking, and the pollSkeleton method simply blocks until the next frame is available.
This seems like a reasonable assumption, since the SDK supports both polling and event based notifications, implying that if you don't poll, something else will trigger the fetch of the next frame and send you the event.
If this is the case, then you can solve your problem by simply scanning the timestamp on the skeleton data that's available from the thread...
System.Int64 lastSkeletonTime = 0;
public bool pollSkeleton ()
{
if (kinect.getSkeleton().liTimeStamp > lastSkeletonTime) {
updatedSkeleton = true;
newSkeleton = true;
System.Int64 cur = kinect.getSkeleton().liTimeStamp;
System.Int64 diff = cur - lastSkeletonTime;
deltaTime = diff / (float)1000;
lastSkeletonTime = cur;
processSkeleton();
}
return newSkeleton;
}
If this doesn't work, then most likely you will need to launch a background thread, or switch to handling the events.

Related

Random Bug in Unity2D

I've been working on a project for the GMTK 2022 Game Jam recently, and I ran into a very strange problem. I have a dash that starts when you are moving and press space. It moves you in the direction of your velocity, then for a short time lets you move very quickly. It works perfectly fine in all cases, unless the direction you are moving is up and to the left, in which case, the if statement strangely won't trigger. I'm sure this is something idiotic, but I've been troubleshooting it for the last hour and it's been driving me insane.
// Update is called once per frame
void Update()
{
playerInputh = 0;
playerInputv = 0;
if (Input.GetKey("right"))
{
playerInputh = 1;
}
if (Input.GetKey("left"))
{
playerInputh = -1;
}
if (Input.GetKey("right") && Input.GetKey("left"))
{
playerInputh = 0;
}
if (Input.GetKey("up"))
{
playerInputv = 1;
}
if (Input.GetKey("down"))
{
playerInputv = -1;
}
if (Input.GetKey("up") && Input.GetKey("down"))
{
playerInputv = 0;
}
Vector2 screenPosition = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
Vector2 mouseWorldPosition = Camera.main.ScreenToWorldPoint(screenPosition);
//This is the dash that isn't working:
if ((Input.GetKeyDown(/*"right shift"*/"space")) && (playerInputh != 0 || playerInputv != 0))
{
Debug.Log("Dash");
//Vector2 transform2dposition = new Vector2(transform.position.x, transform.position.y);
m_Rigidbody.AddForce((m_Rigidbody.velocity) * 500f);
wJumpTimer = airControlAfterJump;
speed = maxSpeed*3.5f;
StartCoroutine(Roll());
}
}
void FixedUpdate()
{
//no moving while jumping!!!
if (wJumpTimer > 0)
{
wJumpTimer -= 1;
}
else
{
wJumpTimer = 0;
}
//move
if (playerInputh != 0 && playerInputv != 0) //make diagonals no super sayan
{
playerInputh *= moveLimiter;
playerInputv *= moveLimiter;
one_h = playerInputh;//futureproof
one_v = playerInputv;
}
if ((playerInputh != 0 || playerInputv != 0) && speed < maxSpeed) //are we hitting the move buttons??
{
speed += acceleration;//accelerate
one_h = playerInputh;//futureproof
one_v = playerInputv;
}
else
{
if (speed > 0f) //are we getting off the ride
{
speed -= deceleration; //decelerate
}
else
{
speed = 0f; //no funny buisness
}
}
m_Rigidbody.velocity = new Vector2(one_h * speed, one_v * speed); //actually move
}
void SetFace(int diceNumb)
{
rndr.sprite = sprites[diceNumb];
}
IEnumerator Roll()
{
Random diceNumb = new Random();
rndr.sprite = sprites[diceNumb.Next(0,5)];
yield return new WaitForSeconds(0.125f);
rndr.sprite = sprites[diceNumb.Next(0, 5)];
yield return new WaitForSeconds(0.125f);
rndr.sprite = sprites[diceNumb.Next(0, 5)];
yield return new WaitForSeconds(0.125f);
rndr.sprite = sprites[diceNumb.Next(0, 5)];
yield return new WaitForSeconds(0.125f);
var newValue = diceNumb.Next(0, 5);
FaceValue = newValue + 1;
rndr.sprite = sprites[newValue];
}
How are your inputs setup? I am suspecting you have one key bound to multiple actions, like one key is set up as primary for an action and alternate for another action.
Personally I'd also stop using the assignment operators and increment instead. Instead of
playerInputh = 0;
if (Input.GetKey("right"))
{
playerInputh = 1;
}
if (Input.GetKey("left"))
{
playerInputh = -1;
}
if (Input.GetKey("right") && Input.GetKey("left"))
{
playerInputh = 0;
}
you can do
playerInputv = 0;
if (Input.GetKey("right"))
{
playerInputh += 1;
}
if (Input.GetKey("left"))
{
playerInputh += -1;
}
Net result is the same here - if you push both keys the result sums to zero, but the code is easier to read (IMO).
When you check things for key bindings also check alternates for space, because that's another one of the triggers you need to dash.

How to display Text during Unity game?

I inherited by my ex collegue a Unity game. I need to display a label in my scene. The label should be contains the text "what is your level of fear?"
The user should be throught Logitech steering wheel, using the rotation button select a number from 0 to 9.
Now I m new user of unity and I want to start this task step by step. My Unity project have a Init scene that can start other scenes.
In my Init scene I create this static component (FearTest):
This component have a RedDot (a simple red dot) and textAnsia2 that contains the text that I need to display.
This is my class FreatTest:
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace DrivingTest
{
public class FearTest : MonoBehaviour
{
//public GameObject redDot;
public TextMeshPro textAnsia2;
public TextMesh textAnsia3;
public int shouldEvaluateTest = 0; // 0 - false, 1 - true
public int secondsInterval;
public int secondsToDisaply;
public int radiusR1;
public int radiusR2;
public int reactionTestDuration;
public int maxDots;
public float dotRadius = 1;
private float endTime;
private float dotDisaplyAtTime;
private List<float> dotDisplayReactTime = new List<float>();
private int missedSignals;
private int currentDotsCount;
private bool isReactionAlreadyGiven;
public bool ShouldEvaluateTest
{
get
{
return shouldEvaluateTest != 0;
}
}
void OnEnable()
{
GameManager.TestSceneLoaded += OnSceneLoaded;
}
private void OnDisable()
{
GameManager.TestSceneLoaded -= OnSceneLoaded;
}
#region Scene Loaded
private void OnSceneLoaded(eTESTS test)
{
if (SceneManager.GetActiveScene().name != test.ToString())
return;
Reset();
GetApiParams();
}
#endregion
private void GetApiParams()
{
#if UNITY_EDITOR
if (!GameManager.Instance)
{
Init();
return;
}
#endif
Debug.Log("called rt");
NextTestOutput nextTestOutput = GameManager.Instance.currentTest;
if (nextTestOutput.content != null && nextTestOutput.content.Count > 0)
{
foreach (var item in nextTestOutput.content[0].listInputParameter)
{
switch ((ePARAMETERS)item.id)
{
case ePARAMETERS.RT_ENABLE:
shouldEvaluateTest = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.RT_DURATION:
reactionTestDuration = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.RED_DOT_FREQ:
secondsInterval = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.RED_DOT_MAX_COUNT:
maxDots = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.RED_DOT_RADIUS_R1:
radiusR1 = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.RED_DOT_RADIUS_R2:
radiusR2 = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.RED_DOT_SIZE:
dotRadius = float.Parse(item.Value)/10f;
break;
case ePARAMETERS.RED_DOT_TIME:
secondsToDisaply = System.Convert.ToInt32(item.Value);
break;
}
}
Debug.Log("called rt "+ shouldEvaluateTest);
/*if (ShouldEvaluateTest)
{
Init();
}*/
Init();//dopo bisogna sistemare il shoudl evaluatetest
}
}
Coroutine displayCoroutine;
public void Init()
{
endTime = Time.time + reactionTestDuration + 10;
SetRedDotSize();
displayCoroutine = StartCoroutine(RedDotDisplay());
}
private IEnumerator RedDotDisplay()
{
yield return new WaitForSeconds(2);
while (true)
{
SetRandomDotPosition();
RedDot(true);
isReactionAlreadyGiven = false;
dotDisaplyAtTime = Time.time;
currentDotsCount++;
yield return new WaitForSeconds(secondsToDisaply);
if(!isReactionAlreadyGiven)
{
missedSignals++;
}
RedDot(false);
if ((reactionTestDuration > 0 && endTime <= Time.time) || (maxDots > 0 && currentDotsCount >= maxDots))
break;
float waitTime = secondsInterval - secondsToDisaply;
yield return new WaitForSeconds(waitTime);
}
}
private void Update()
{
if (!ShouldEvaluateTest)
return;
if (!isReactionAlreadyGiven && /*redDot.activeSelf &&*/ (LogitechGSDK.LogiIsConnected(0) && LogitechGSDK.LogiButtonIsPressed(0, 23)))
{
isReactionAlreadyGiven = true;
float reactionTime = Time.time - dotDisaplyAtTime;
Debug.Log("Reaction Time RT : " + reactionTime);
RedDot(false);
dotDisplayReactTime.Add(reactionTime);
}
}
public double GetReactionTimeAvg()
{
double avg = 0;
foreach (var item in dotDisplayReactTime)
{
avg += item;
}
//avg / currentDotsCount
return avg / (float)dotDisplayReactTime.Count;
}
public double GetMissedSignals()
{
return ((float) missedSignals / (float) currentDotsCount) * 100;
}
private void RedDot(bool state)
{
//redDot.SetActive(state);
textAnsia2.SetText("Pippo 2");
}
private void SetRedDotSize()
{
//redDot.transform.localScale *= dotRadius;
textAnsia2.transform.localScale *= dotRadius;
textAnsia3.transform.localScale *= dotRadius;
}
private void SetRandomDotPosition()
{
//redDot.GetComponent<RectTransform>().anchoredPosition = GetRandomPointBetweenTwoCircles(radiusR1, radiusR2)*scale;
float scale = ((Screen.height*0.9f) / radiusR2) * 0.9f;
Vector3 pos = GetRandomPointBetweenTwoCircles(radiusR1, radiusR2) * scale;
Debug.Log("RT temp pos : " + pos);
pos = new Vector3(500, 500, 0);
Debug.Log("RT pos : " + pos);
// redDot.transform.position = pos;
Vector3 pos2 = new Vector3(20, 20, 0);
Debug.Log("text ansia 2 : " + pos2);
textAnsia2.transform.position = pos2;
textAnsia3.transform.position = pos;
}
#region Getting Red Dot b/w 2 cricles
/*
Code from : https://gist.github.com/Ashwinning/89fa09b3aa3de4fd72c946a874b77658
*/
/// <summary>
/// Returns a random point in the space between two concentric circles.
/// </summary>
/// <param name="minRadius"></param>
/// <param name="maxRadius"></param>
/// <returns></returns>
Vector3 GetRandomPointBetweenTwoCircles(float minRadius, float maxRadius)
{
//Get a point on a unit circle (radius = 1) by normalizing a random point inside unit circle.
Vector3 randomUnitPoint = Random.insideUnitCircle.normalized;
//Now get a random point between the corresponding points on both the circles
return GetRandomVector3Between(randomUnitPoint * minRadius, randomUnitPoint * maxRadius);
}
/// <summary>
/// Returns a random vector3 between min and max. (Inclusive)
/// </summary>
/// <returns>The <see cref="UnityEngine.Vector3"/>.</returns>
/// <param name="min">Minimum.</param>
/// <param name="max">Max.</param>
Vector3 GetRandomVector3Between(Vector3 min, Vector3 max)
{
return min + Random.Range(0, 1) * (max - min);
}
#endregion
#region Reset
private void Reset()
{
if (displayCoroutine != null)
StopCoroutine(displayCoroutine);
RedDot(false);
shouldEvaluateTest = 0;
reactionTestDuration = 0;
secondsInterval = 0;
missedSignals = 0;
maxDots = 0;
radiusR1 = 0;
radiusR2 = 0;
dotRadius = 1;
secondsToDisaply = 0;
endTime = 0;
dotDisaplyAtTime = 0;
dotDisplayReactTime.Clear();
//redDot.transform.localScale = Vector3.one;
textAnsia2.transform.localScale = Vector3.one;
textAnsia3.transform.localScale = Vector3.one;
currentDotsCount = 0;
isReactionAlreadyGiven = true;
}
#endregion
}
}
When "SetRandomDotPosition" method is called, in my scene I can display in a random position of the screen the RedDot (that is a simple image with a red dot), and I need to display also my text.
The red dot is displayed, but I m not able to display the text.
How can I fixed it ?

Array inaccessible only at certain times

I am currently building a parallax background effect in Unity and starting with some starter code that I'm trying to work though. The code works for the most part, with some small issues I'm slowly getting through.
I have been testing out various stats of public vs. private, etc. but can't seem to work out exactly why and where the issue I'm facing is occurring.
When I run the same script and it calls Update() every frame, I would expect to have the same size "poolObjects" length. However, when I call it with a starting pool size of 10, I get 10, then 2, then 0, then 10, 2, 0, etc.
I am truncating it but posting what I think to be relevant here. Hope you can help me see what is likely obvious!
I do see "In GetPool Object" but because the length is never > 0, I never see within the For Loop, which is essential. I can't figure out why the poolObjects length would be showing up as 0.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Parallaxer : MonoBehaviour {
class PoolObject {
public Transform transform;
public bool inUse;
public PoolObject(Transform t) { transform = t; }
public void Use() { inUse = true; }
public void Dispose() { inUse = false; }
}
...
public int poolSize;
public float shiftSpeed;
public float spawnRate;
...
float spawnTimer;
PoolObject[] poolObjects;
float targetAspect;
GameManager game;
void Awake() {
Configure();
}
void Start() {
game = GameManager.Instance;
}
...
void Update() {
Debug.Log(poolObjects.Length + "Len here of pool objects");
if (game.GameOver) return;
Shift();
spawnTimer += Time.deltaTime;
if (spawnTimer > spawnRate) {
Spawn();
spawnTimer = 0;
}
}
void Configure() {
//spawning pool objects
targetAspect = targetAspectRatio.x / targetAspectRatio.y;
// targetAspect = Camera.main.aspect;
// Debug.Log(targetAspectRatio.x +" " + targetAspectRatio.y);
poolObjects = new PoolObject[poolSize];
for (int i = 0; i < poolObjects.Length; i++) {
GameObject go = Instantiate(Prefab) as GameObject;
Transform t = go.transform;
t.SetParent(transform);
t.position = Vector3.one * 1000;
poolObjects[i] = new PoolObject(t);
}
if (spawnImmediate) {
SpawnImmediate();
}
}
void Spawn() {
//moving pool objects into place
Transform t = GetPoolObject();
Debug.Log("In to Spawn" + t);
if (t == null) return;
Vector3 pos = Vector3.zero;
pos.y = Random.Range(ySpawnRange.minY, ySpawnRange.maxY);
pos.x = (defaultSpawnPos.x * Camera.main.aspect) / targetAspect;
// Debug.Log("Spwaning");
// Debug.Log(Camera.main.aspect);
// Debug.Log(immediateSpawnPos.x + " " + immediateSpawnPos.y + " " + targetAspect);
t.position = pos;
// Debug.Log(pos);
}
void SpawnImmediate() {
Transform t = GetPoolObject();
if (t==null) return;
Vector3 pos = Vector3.zero;
pos.y = Random.Range(ySpawnRange.minY, ySpawnRange.maxY);
pos.x = (immediateSpawnPos.x * Camera.main.aspect) / targetAspect;
t.position = pos;
Spawn();
}
void Shift() {
//loop through pool objects
//moving them
//discarding them as they go off screen
Debug.Log(poolObjects.Length + "Finding Length");
for (int i = 0; i < poolObjects.Length; i++) {
poolObjects[i].transform.position += -Vector3.right * shiftSpeed * Time.deltaTime;
Debug.Log(poolObjects[i].transform.position);
CheckDisposeObject(poolObjects[i]);
}
}
void CheckDisposeObject(PoolObject poolObject) {
//place objects off screen
if (poolObject.transform.position.x < (-defaultSpawnPos.x * Camera.main.aspect) / targetAspect) {
poolObject.Dispose();
poolObject.transform.position = Vector3.one * 1000;
}
}
Transform GetPoolObject() {
//retrieving first available pool object
Debug.Log("In GetPool Object" + poolObjects.Length);
for (int i = 0; i < poolObjects.Length; i++) {
Debug.Log("This is not showing up "+ poolObjects[i].inUse);
if (!poolObjects[i].inUse) {
poolObjects[i].Use();
return poolObjects[i].transform;
}
}
return null;
}
}

Enemy appear behind player chance 1 on 5

I've written a script that makes the enemy appear behind the player, with a 1 in 5 chance every 3 minutes, I've changed the 1 in 5 chance to a 1 in 1 chance for testing purposes, but I'am unsure if it works.
I know how to place the enemy behind the player but not with a chance of 1 on 5.
I've tried this with a random number between 1 and 5. And that if the random number equals 1 he has to spawn the enemy and else not.
This is the code:
using UnityEngine;
using System.Collections;
public class MainEnemy : MonoBehaviour
{
public GameObject Main;
public GameObject Holder;
public Transform player;
public Transform FPSController;
bool wantPosition;
bool appear;
float timer;
Vector3 temp;
// Use this for initialization
void Start()
{
wantPosition = true;
appear = false;
temp = new Vector3(0, 0, 0);
timer = 20;// 180;
}
// Update is called once per frame
void Update()
{
Appear();
}
Vector3 GetPosition()
{
Debug.Log("Hij doet het getposition");
float px = player.transform.position.x;
float pz = player.transform.position.z;
//
float angle2 = Mathf.Cos(Camera.main.transform.eulerAngles.y) + 180;
//
float distance = 20;
float distancex = Mathf.Cos(angle2 + 180) * distance;
float distancez = Mathf.Sin(angle2 + 180) * distance;
// Tell distanceen bij coordinaten op
temp = new Vector3(distancex, -3, distancez);
return temp;
}
void SetFalse()
{
if (timer < 1)
{
timer = 10;// 180; // na 3 minuten weer kijken of hij hem laat zien
}
Main.SetActive(false);
}
void Position()
{
if (wantPosition)
{
temp = GetPosition();
Holder.transform.position = player.position + temp;
Main.SetActive(true);
if (timer < 0)
{
timer = 10; // kort zichtbaar
}
wantPosition = false;
}
}
bool Appeared()
{
bool appear = false;
int rand = Random.Range(1, 1);
if (rand == 1)
{
appear = true;
}
else
{
appear = false;
}
return appear;
}
void Appear()
{
bool appear = false;
if (timer <= 0)
{
appear = Appeared();
}
else
{
timer -= Time.deltaTime;
if (timer < 10)
{
Debug.Log(timer);
}
}
if (appear == true)
{
Position();
}
else
{
SetFalse();
}
}
}
Firstly be aware that Random.Range returns a number that is greater than or equal to min, but less than (but not equal to) max. If the special case where min == max, then that value will be returned.
Secondly your Appeared function is a bit verbose. Assuming we set it back to a 1 in 5 chance, you'd have this:
bool Appeared()
{
bool appear = false;
int rand = Random.Range(1, 6);
if (rand == 1)
{
appear = true;
}
else
{
appear = false;
}
return appear;
}
Firstly let me point out that saying:
if(x)
y = true
else
y = false
Where x is a Boolean condition, is exactly the same as saying
y = x
So perhaps you'd change it to:
bool Appeared()
{
bool appear = false;
int rand = Random.Range(1, 6);
appear = rand == 1;
return appear;
}
Now look, why bother setting appear to false at the start? That value never gets used. Perhaps this:
bool Appeared()
{
int rand = Random.Range(1, 6);
bool appear = rand == 1;
return appear;
}
Hmm, now we're just assigning a variable and then returning it on the next line. OK, maybe this:
bool Appeared()
{
int rand = Random.Range(1, 6);
return rand == 1;
}
Right, yeah. This looks familiar. Now, again, we're almost just assigning a variable and then returning it on the next line. Do we really need that rand variable now? Probably not. How about this then:
bool Appeared()
{
return Random.Range(1, 6) == 1;
}
Much better.
An easy and performant way to get a 1/5 chance in Unity3D is
if(Random.value < 1f/5f)
//do something
Random.value is returning a float 0 <= value <= 1
**EDIT Updated for UnityEngine
Change your bool Appeared method to:
bool Appeared()
{
bool appear = false;
int rand = Random.Range(1,6)
if (rand == 1)
{
appear = true;
} // else not needed
return appear;
}

Only one gameobject being affected by script

Making a state machine AI "game" I have 3 states Movement, Combat, and View. They all work very well, now I'm making a flee (by the way if anyone has any good links to tutorials or info that they have off hand I would love that) state so I want to make all three previous state in their Idle mode. Which works perfectly when I have 1 AI. As soon as I put the rest (there is 6 total now) it will still only affect 1, but then after some time it will "release" I guess it the best term the script and just goes back to the three states that are supposed to be idle. I think that in my Coin script I am saying find and I think it needs to be something else, I'm just not sure if that's even right but based off of my previous experiences that's usually the case with Unity. I'll post some code, but here's some info that may help your eyes by not having to read through everything:
-I have a bool set up in my AIClass that is false until one of my gameobjects collides with a 'coin', and coin is jsut a gameobject i put in my game to test out my AI behaviors. When that bool is true, in my update in my AIClass(sorry it's at the very bottom of the script you can scroll down, but I had to put other code just in case I was doing something in that to cause my problem.) It sets the state of MovementState to Idle, CombatState to Idle and ViewState to Idle.
-Also, when that bool is false is the only time that the call for the transition of states occur. For example, normally the AI will search around trying to find player, if they find him they go near him, and if they are in a certain distance they fire. Pretty simple. The movement between those states happens at the end of each IENumerator when the 'NextMovementState()' or 'NextCombatState()' or 'NextViewState()' are called. So when the bool is true those should never be called, halting the AI from moving to another state other than Idle (to what it's set at when the bool is true in the update).
And this works fine when I have only 1 AI in the game.
CoinScript.cs
using UnityEngine;
using System.Collections;
public class CoinScript : MonoBehaviour {
private AIClass a;
// Use this for initialization
void Start () {
a = GameObject.Find ("Enemy").GetComponent<AIClass>();
}
// Update is called once per frame
void Update () {
}
void OnCollisionEnter(Collision collision)
{
if (collision.collider)
{
if(collision.gameObject.tag == "Enemy" || collision.gameObject.tag == "EnemyProjectile")
{
Physics.IgnoreCollision(rigidbody.collider,collision.collider);
//Debug.Log ("Enemy");
}
if(collision.gameObject.tag == "Player")
{
Debug.Log ("triggered!");
a.fleeBool = true;
Destroy(gameObject);
}
}
}
}
AIClass.cs
using UnityEngine;
using System.Collections;
public class AIClass : MonoBehaviour
{
public NavMeshAgent agent;
//Ammo in gun before reloading is required
public int ammo = 30;
public int maxAmmo = 30;
//Number of bullets a gun fires in sequence on 1 trigger pull
public int chamber = 3;
public int maxChamber = 3;
//Pause between bursts or mouse presses, set to 0 for fully automatic
public double chamberTime = 120;
//How fast a gun fires in RPS
public int fireRate = 7;
public int fireTimer = 0;
//How fast a gun can reload
public int reloadTime = 3;
public int reloadTimer = 0;
//Number of bullets fired per shot
public int bulletsFired = 1;
public GameObject bulletClone;
//Acceptable degrees as to which the AI will begin firing at its target
public int firingAngle = 5;
//Vision cone of degrees to left and right
public int visionAngle = 35;
public int visionDistance = 100;
public int vRotationSpeed = 3;
public int vIdleTimer = 0;
public int vIdleTime = 300;
public int searchTimer = 0;
public int searchTime = 300;
public int mIdleTimer = 0;
public int mIdleTime = 300;
public bool isFocusedOnPlayer = false;
public bool seesPlayer = false;
//
public bool fleeBool;
public bool flee;
public enum MovementState
{
MSearch,
MMoving,
MIdle,
}
public enum CombatState
{
CFiring,
CReloading,
CIdle,
}
public enum ViewState
{
VSearch,
VFocus,
VIdle,
}
public enum FleeState
{
FSearch,
FMoving,
FIdle
}
public CombatState combatState;
public ViewState viewState;
public MovementState movementState;
public FleeState fleeState;
//Search state (knows where player is and will head to the player's location)
IEnumerator MSearchState ()
{
mIdleTimer = 0;
int stuckTimer = 0;
while (movementState == MovementState.MSearch)
{
//I've arrived at my location, if idle too long, then go back to idle state
if(Vector3.Distance(transform.position,agent.destination) < 3)
mIdleTimer++;
//I'm stuck and haven't moved in a while, go back to idle state
if(agent.velocity.magnitude < 1)
stuckTimer++;
if(seesPlayer || mIdleTimer > mIdleTime + 200 || stuckTimer > 300)
{
agent.destination = transform.position;
movementState = MovementState.MIdle;
}
yield return 0;
}
if (!flee) {
NextMovementState();
}
}
//Wander state
IEnumerator MMovingState ()
{
while (movementState == MovementState.MMoving)
{
//Wander code... Create a random angle and convert it to radians
float randomAngle = (float)(3.14/180)*Random.Range(0,360);
//Normalize direction vector, as we will be using it to calculate where we place the circle
Vector3 tempV = agent.velocity;
Vector3.Normalize (tempV);
//Using our relative position, 5 units in front of us. Use the generated angle to find the point on the circle that we want to go to
agent.destination = transform.position + tempV * 3 + new Vector3(Mathf.Cos (randomAngle)*3,0,Mathf.Sin (randomAngle)*3);
//Check to see if we are within the arena bounds, if not, push our projected vector back inside
if(agent.destination.x > 24)
agent.destination = agent.destination + new Vector3(-7,0,0);
if(agent.destination.x < -24)
agent.destination = agent.destination + new Vector3(7,0,0);
if(agent.destination.z > 24)
agent.destination = agent.destination + new Vector3(0,0,-7);
if(agent.destination.z < -24)
agent.destination = agent.destination + new Vector3(0,0,7);
if(seesPlayer)
{
agent.destination = transform.position;
movementState = MovementState.MIdle;
}
yield return 0;
}
if (!flee) {
NextMovementState ();
}
}
//Not moving, if I don't see the player for awhile, then go wander
IEnumerator MIdleState ()
{
mIdleTimer = 0;
while (movementState == MovementState.MIdle)
{
if(seesPlayer)
mIdleTimer = 0;
else
mIdleTimer++;
if(mIdleTimer > mIdleTime + Random.Range (-100,100))
movementState = MovementState.MMoving;
yield return 0;
}
if (!flee) {
NextMovementState ();
}
}
//Visual search state, randomly look around and check to see if we see the player
IEnumerator VSearchState ()
{
Transform target = GameObject.FindWithTag ("Player").transform;
Vector3 targetPosition = target.position;
while (viewState == ViewState.VSearch)
{
searchTimer--;
//Vision Cone calculation
Vector3 targetDir = target.position - transform.position;
Vector3 forward = transform.forward;
float angle = Vector3.Angle(targetDir, forward);
//If player is within vision cone then proceed
if (angle < visionAngle)
{
//Check to see if there are any object between player and myself
RaycastHit hit;
if (Physics.Raycast(transform.position, targetDir, out hit))
{
if(hit.transform == target)
{
seesPlayer = true;
viewState = ViewState.VFocus;
}
}
}
//Look in another direction
if(searchTimer < 0)
{
searchTimer = searchTime + Random.Range (-100,100);
targetPosition = new Vector3(Random.Range (-100,100),transform.position.y,Random.Range (-100,100));
}
transform.rotation = Quaternion.Slerp(transform.rotation,Quaternion.LookRotation(targetPosition - transform.position), vRotationSpeed*Time.deltaTime);
yield return 0;
}
if (!flee) {
NextViewState ();
}
}
//Focus on player
IEnumerator VFocusState ()
{
Transform target = GameObject.FindWithTag ("Player").transform;
while (viewState == ViewState.VFocus)
{
//Vision Cone calculation
Vector3 targetDir = target.position - transform.position;
Vector3 forward = transform.forward;
float angle = Vector3.Angle(targetDir, forward);
if (angle > visionAngle)
viewState = ViewState.VIdle;
else
{
RaycastHit hit;
//Check if there are any objects in the way
if (Physics.Raycast(transform.position, targetDir, out hit))
{
if(hit.transform == target)
{
//Tell other AI where player is
GameObject[] objArray = GameObject.FindGameObjectsWithTag ("Enemy");
AIClass[] enemyArray = new AIClass[objArray.Length];
for(int i = 0; i < enemyArray.Length; i++)
{
enemyArray[i] = (AIClass)objArray[i].GetComponent(typeof(AIClass));
if(i >= enemyArray.Length/2)
enemyArray[i].agent.destination = target.position;
else
enemyArray[i].agent.destination = target.position + target.forward*5;
enemyArray[i].movementState = MovementState.MSearch;
}
seesPlayer = true;
transform.rotation = Quaternion.Slerp(transform.rotation,Quaternion.LookRotation(target.position - transform.position), vRotationSpeed*Time.deltaTime);
//Check to see player is within sights of the gun
if (angle < firingAngle)
isFocusedOnPlayer = true;
else
isFocusedOnPlayer = false;
}
else
{
//I no longer see the player
seesPlayer = false;
viewState = ViewState.VIdle;
isFocusedOnPlayer = false;
}
}
}
yield return 0;
}
if (!flee) {
NextViewState ();
}
}
//Visual idle state, basically the ai is just looking forward
IEnumerator VIdleState ()
{
vIdleTimer = 0;
Transform target = GameObject.FindWithTag ("Player").transform;
while (viewState == ViewState.VIdle)
{
//Vision cone calculation
vIdleTimer++;
Vector3 targetDir = target.position - transform.position;
Vector3 forward = transform.forward;
float angle = Vector3.Angle(targetDir, forward);
//Check to see if there is an object is between the ai and the player
if (angle < visionAngle)
{
RaycastHit hit;
if (Physics.Raycast(transform.position, targetDir, out hit))
{
if(hit.transform == target)
{
seesPlayer = true;
viewState = ViewState.VFocus;
}
}
}
if(vIdleTimer > vIdleTime)
viewState = ViewState.VSearch;
yield return 0;
}
if (!flee) {
NextViewState ();
}
}
//Firing gun state
IEnumerator CFiringState ()
{
while (combatState == CombatState.CFiring)
{
if(!isFocusedOnPlayer)
combatState = CombatState.CIdle;
fireTimer--;
if(ammo > 0)
{
if(chamber > 0)
{
if(fireTimer <= 0)
{
for(int i = 0; i < bulletsFired;i++)
{
GameObject temp = (GameObject) Instantiate (bulletClone,transform.position + transform.forward,transform.rotation);
temp.rigidbody.AddForce(transform.forward*500);
}
fireTimer = 60 / fireRate;
ammo--;
chamber--;
}
}
else
{
chamber = maxChamber;
fireTimer = (int)(60/chamberTime);
}
}
else
{
combatState = CombatState.CReloading;
}
yield return 0;
}
if (!flee) {
NextCombatState ();
}
}
IEnumerator CReloadingState ()
{
reloadTimer = reloadTime * 60;
while (combatState == CombatState.CReloading)
{
reloadTimer--;
if(reloadTimer <= 0)
{
ammo = maxAmmo;
combatState = CombatState.CIdle;
}
yield return 0;
}
if (!flee) {
NextCombatState ();
}
}
IEnumerator CIdleState ()
{
while (combatState == CombatState.CIdle)
{
if(isFocusedOnPlayer)
combatState = CombatState.CFiring;
yield return 0;
}
if (!flee) {
NextCombatState ();
}
}
void Start ()
{
fleeBool = false;
flee = false;
NextCombatState();
NextViewState();
NextMovementState();
}
void NextMovementState()
{
string methodName = movementState.ToString() + "State";
System.Reflection.MethodInfo info = GetType().GetMethod(methodName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
StartCoroutine((IEnumerator)info.Invoke(this, null));
}
void NextCombatState ()
{
string methodName = combatState.ToString() + "State";
System.Reflection.MethodInfo info = GetType().GetMethod(methodName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
StartCoroutine((IEnumerator)info.Invoke(this, null));
}
void NextViewState ()
{
string methodName = viewState.ToString() + "State";
System.Reflection.MethodInfo info = GetType().GetMethod(methodName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
StartCoroutine((IEnumerator)info.Invoke(this, null));
}
void NextFleeState()
{
string methodName = viewState.ToString() + "State";
System.Reflection.MethodInfo info = GetType().GetMethod(methodName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
StartCoroutine((IEnumerator)info.Invoke(this, null));
}
void Update()
{
Debug.DrawLine(transform.position, transform.position+transform.forward*5, Color.red);
if (fleeBool == true)
{
flee = true;
}
if (flee == true)
{
Debug.Log ("flee is true");
combatState = CombatState.CIdle;
movementState = MovementState.MIdle;
viewState = ViewState.VIdle;
Debug.Log ("End of idles");
}
}
}
You can use GameObject.FindObjectsOfType() or GameObject.FindGameObjectsWithTag
you can do that with store all the AIClass into array and then loop it become specific, so every enemy can affected
AIClass[] AIObject = GameObject.Find("Enemy").GetComponents<AIClass>();
foreach(AIClass a in AIObject ){
//do something with a
}
it should work.

Categories