/// @brief Method to change the number of players /// /// @note This method changes the player list. It will unselect all players and /// old player references might become invalid or irrelevant! This method also assumes that the /// player manager has already been initialized. /// @param newNum The new number of players /// @return True on success, false otherwise. public virtual bool ChangeNumberOfPlayers(int newNum) { if (newNum < 0) { return(false); } if (newNum == m_MaxNumberOfPlayers) { return(true); // nothing to do... } for (int i = 0; i < m_players.Count; i++) { UnselectPlayer(i); } if (newNum < m_MaxNumberOfPlayers) { while (m_players.Count > newNum) { m_players.RemoveAt(m_players.Count - 1); } } else { while (m_players.Count < newNum) { NISelectedPlayer newPlayer = new NISelectedPlayer(); newPlayer.User = null; m_players.Add(newPlayer); } } m_MaxNumberOfPlayers = newNum; return(true); // success }
/// mono-behavior fixedUpdate /// This is used to update the position for the previous frame. /// @note we use FixedUpdate because this can be updated multiple times during a frame. public void FixedUpdate() { NISelectedPlayer player = m_playerManager.GetPlayer(m_playerNum); if (player == null || player.Valid == false || player.Tracking == false) { return; // no player to work with } SkeletonJointPosition skelJointPos; if (player.GetSkeletonJointPosition(m_jointToUse, out skelJointPos)) { m_lastFrameCurPoint.UpdatePoint(skelJointPos.Position, skelJointPos.Confidence); } if (m_startingPoseReferenceType == StartingPosReferenceType.StaticModifierToOtherJoint || m_startingPoseReferenceType == StartingPosReferenceType.ScaledModifierToOtherJoint) { if (player.GetSkeletonJointPosition(m_referenceJoint, out skelJointPos)) { m_lastFrameReferenceJoint.UpdatePoint(skelJointPos.Position, skelJointPos.Confidence); } } if (m_startingPoseReferenceType == StartingPosReferenceType.ScaledModifierToOtherJoint) { SkeletonJointPosition skelJointPos2; if (player.GetSkeletonJointPosition(m_referenceJointScale1, out skelJointPos) && player.GetSkeletonJointPosition(m_referenceJointScale2, out skelJointPos2)) { m_lastFrameScaleJoint1.UpdatePoint(skelJointPos.Position, skelJointPos.Confidence); m_lastFrameScaleJoint2.UpdatePoint(skelJointPos2.Position, skelJointPos2.Confidence); } } }
/// @brief Gets the starting position if @ref m_startingPoseReferenceType is TrackedJointReference /// /// @return The reference position of the tracked joint (zero if there is a problem). protected Vector3 GetStartingPosFromTrackedJoint() { // The position we count from is the initial calibration pose. NISelectedPlayer player = m_playerManager.GetPlayer(m_playerNum); if (player == null || player.Valid == false) { return(Vector3.zero); // no position. } SkeletonJointTransformation skelTrans; if (player.GetReferenceSkeletonJointTransform(m_jointToUse, out skelTrans) == false) { return(Vector3.zero); // we don't have the transform } Point3D pos = skelTrans.Position.Position; Vector3 res = NIConvertCoordinates.ConvertPos(pos); if (skelTrans.Position.Confidence < 0.5f || float.IsNaN(res.magnitude) || res.z >= 0) { // this means the value is irrelevant. player.RecalcReferenceJoints(); SkeletonJointPosition skelJointPos; if (player.GetSkeletonJointPosition(m_jointToUse, out skelJointPos) == false) { return(Vector3.zero); } res = NIConvertCoordinates.ConvertPos(skelJointPos.Position); } return(res); }
/// mono-behavior update (called once per frame) public void Update() { if (m_playerManager == null || m_playerManager.Valid == false) { return; // we can do nothing. } NISelectedPlayer player = m_playerManager.GetPlayer(m_playerNumber); if (player == null || player.Valid == false || player.Tracking == false) { RotateToCalibrationPose(); // we don't have anything to work with. return; } Vector3 skelPos = Vector3.zero; SkeletonJointTransformation skelTrans; if (player.GetReferenceSkeletonJointTransform(SkeletonJoint.Torso, out skelTrans)) { if (skelTrans.Position.Confidence < 0.5f) { player.RecalcReferenceJoints(); // we NEED the torso to be good. } if (skelTrans.Position.Confidence >= 0.5f) { skelPos = NIConvertCoordinates.ConvertPos(skelTrans.Position.Position); } } UpdateSkeleton(player, skelPos); }
/// used for updating every frame /// @note While we are still steady (i.e. we haven't gotten a "not steady" event) we update /// the time and frame every frame! public override void UpdateFrame() { if (m_context == null || m_context.UserSkeletonValid == false) { return; // no data; } NISkeletonTracker tracker = m_pointTracker as NISkeletonTracker; if (tracker.Valid == false) { return; // no one to track. } NISelectedPlayer player = tracker.GetTrackedPlayer(); if (player == null || player.Valid == false) { return; } NIUserAndSkeleton.NIPoseDetectionStateStatus curStatus = new NIUserAndSkeleton.NIPoseDetectionStateStatus(); if (m_context.UserGenrator.GetPoseStatus(player.OpenNIUserID, m_poseName, ref curStatus) == false) { return; // we do not have good pose information } if (curStatus.m_eState != PoseDetectionState.InPose) { return; } if (curStatus.m_timePoseHeld >= m_timeToHoldPose) { InternalFireDetectEvent(); } }
// protected methods protected override bool InternalInit(NIPointTracker hand) { NISkeletonTracker curHand = hand as NISkeletonTracker; if (curHand == null) { return(false); } if (m_context == null || m_context.UserSkeletonValid == false) { return(false); } NISelectedPlayer player = curHand.GetTrackedPlayer(); if (player == null) { return(false); // no player } player.m_userChangeEventHandler += PlayerUserChangeHandler; if (player.Valid) { validRequestedPoseDetection = m_context.UserGenrator.RequestPoseDetection(m_poseName, player.OpenNIUserID); return(validRequestedPoseDetection); } validRequestedPoseDetection = false; return(true); }
public void UpdateBoneDistance(NISelectedPlayer player, SkeletonJointPosition torsoPos) { //initial adjustment wait... int countBuffer = 6; int countMax = 10; if (countBoneLenghthUpdate >= countMax) { return; } if (countBoneLenghthUpdate <= countBuffer) { countBoneLenghthUpdate++; return; } //bone distance adjustment int count = countBoneLenghthUpdate - countBuffer; if (count == 1)//for waist { //AdjustBone(SkeletonJoint.Waist, SkeletonJoint.Torso, player, torsoPos); //AdjustBone(SkeletonJoint.Neck, SkeletonJoint.Waist, player, torsoPos); AdjustBone(SkeletonJoint.LeftShoulder, SkeletonJoint.RightShoulder, player, torsoPos); AdjustBone(SkeletonJoint.LeftHip, SkeletonJoint.RightHip, player, torsoPos); AdjustBone(SkeletonJoint.LeftElbow, SkeletonJoint.LeftShoulder, player, torsoPos); AdjustBone(SkeletonJoint.LeftHand, SkeletonJoint.LeftElbow, player, torsoPos); AdjustBone(SkeletonJoint.LeftKnee, SkeletonJoint.LeftHip, player, torsoPos); //AdjustBone(SkeletonJoint.LeftFoot, SkeletonJoint.LeftKnee, player, torsoPos); } countBoneLenghthUpdate++; }
private void UpdateVertices(NISelectedPlayer player, SkeletonJointPosition torsoPos) { //initial adjustment wait... int countBuffer = 1; int countMax = 10; if (countBoneLenghthUpdate >= countMax) { return; } if (countBoneLenghthUpdate <= countBuffer) { countBoneLenghthUpdate++; return; } //adjustment int count = countBoneLenghthUpdate - countBuffer; if (count == 2)//for waist { SkinnedMeshRenderer mesh = GetComponentInChildren <SkinnedMeshRenderer>(); Vector3[] vertices = mesh.sharedMesh.vertices; vertices[0].y += 100; } }
public bool UpdateSkeletonData() { if (!IsSkeletonAvailable()) { return(false); // we can do nothing. } NISelectedPlayer player = m_playerManager.GetPlayer(m_playerNumber); // we use the torso as root SkeletonJointTransformation skelTrans; if (player.GetSkeletonJoint(Joint.Torso, out skelTrans) == false) { // we don't have joint information so we simply return... return(false); } // update each joint with data from OpenNI foreach (Joint joint in Enum.GetValues(typeof(Joint))) { SkeletonJointTransformation skelTransJoint; if (player.GetSkeletonJoint(joint, out skelTransJoint) == false) { continue; // irrelevant joint } m_jointData[(int)joint].FromRaw(skelTransJoint); } return(true); }
/// @brief updates the skeleton /// /// This method is responsible for updating the skeleton based on new information. /// It is called by an external manager with the correct user which controls this specific skeleton. /// @param player The player object for the player controlling us. /// @param centerOffset the offset we should use on the center (when moving the root). /// This is usually the starting position (so the skeleton will not "jump" when doing the first update public void UpdateSkeleton(NISelectedPlayer player, Vector3 centerOffset) { if (player.Valid == false) { return; // irrelevant player } if (player.Tracking == false) { return; // not tracking } // we use the torso as root SkeletonJointTransformation skelTrans; if (player.GetSkeletonJoint(SkeletonJoint.Torso, out skelTrans) == false) { // we don't have joint information so we simply return... return; } UpdateRoot(skelTrans.Position, centerOffset); // update each joint with data from OpenNI foreach (SkeletonJoint joint in Enum.GetValues(typeof(SkeletonJoint))) { SkeletonJointTransformation skelTransJoint; if (player.GetSkeletonJoint(joint, out skelTransJoint) == false) { continue; // irrelevant joint } UpdateJoint(joint, skelTransJoint, skelTrans.Position); } }
/// mono-behavior GUI drawing void OnGUI() { Rect curPos = m_basePos; if (m_playerManager == null || m_playerManager.Valid == false) { return; // no player manager so nothing to do... } int numTracking = m_playerManager.GetNumberOfTrackingPlayers(); if (m_AllPlayersMessage && numTracking >= m_playerManager.m_MaxNumberOfPlayers) { return; // all players are tracking, nothing to do here. } if (!m_AllPlayersMessage && numTracking > 0) { return; // at least one player is tracking and we don't want to show the message to the rest } // reaching here means we have a valid player mapper with no calibrated users, we need to // show a message... int numUnselected = 0; for (int i = 0; i < m_playerManager.m_MaxNumberOfPlayers; i++) { NISelectedPlayer player = m_playerManager.GetPlayer(i); if (player == null || player.Valid == false) { GUI.Box(curPos, "Player " + i + " is unselected."); numUnselected++; } else if (player.Tracking == false) { GUI.Box(curPos, "Player " + i + " is calibrating."); } else { continue; } curPos.y += 35; } if (numUnselected == 0) { return; } if (m_actionToSelect.CompareTo("") != 0) { curPos.width = m_actionToSelectRectWidth; GUI.Box(curPos, m_actionToSelect); curPos.y += 35; } if (m_Image != null) { curPos.width = 128; curPos.height = 128; GUI.Box(curPos, m_Image); } }
protected override bool UnsafeUnselectPlayer(int playerNumber) { NISelectedPlayer player = m_players[playerNumber]; NIPlayerPoseCandidateObject user = player.User as NIPlayerPoseCandidateObject; user.m_unselectionOutOfPoseTime = float.MaxValue; // to make sure we can select again! return(base.UnsafeUnselectPlayer(playerNumber)); }
/// @brief Internal method to initialize the players list. /// /// @note The players initialization is done during the mono behavior @ref Awake stage to enable /// calls to GetPlayer during the mono behavior start stage of @b other objects. protected virtual void InitPlayers() { // initialize the players to have an unselected user for each player. m_players = new List <NISelectedPlayer>(); for (int i = 0; i < m_MaxNumberOfPlayers; i++) { NISelectedPlayer player = new NISelectedPlayer(); player.User = null; m_players.Add(player); } }
/// mono-behavior Update is called once per frame public void Update() { if (Time.time < m_timeToCreateNextBall) { return; // we created a ball very recently, wait. } if (m_playerManager == null) { return; // this means we don't even have a plyer manager. } NISelectedPlayer player = m_playerManager.GetPlayer(0); if (player == null || player.Valid == false || player.Tracking == false) { return; // this means we don't have a calibrated user } if (SkeletonGuiControl.m_mode == SkeletonGuiControl.SkeletonGUIModes.GUIMode) { return; // we don't throw balls while in GUI mode. } // now we know we should throw a ball. We first figure out where (a random around the // x axis of the "where" transform and a constant modifier on the y and z). Vector3 pos = where.position; pos.x += Random.Range(-2.0f, 2.0f); pos.y += 8.0f; pos.z += 2.1f; // create the ball Instantiate(prefab, pos, Quaternion.identity); m_numBallsCreated++; // we set the time for the next ball. The time itself depends on how many balls were created // (the more balls, the less time on average). float maxTime = 5.0f; float minTime = 1.0f; if (m_numBallsCreated > 5) { maxTime = 4.0f; } if (m_numBallsCreated > 10) { maxTime = 3.0f; } if (m_numBallsCreated > 15) { minTime = 0.5f; } if (m_numBallsCreated > 20) { maxTime = 2.0f; } m_timeToCreateNextBall = Time.time + Random.Range(minTime, maxTime); }
public Vector3 CalcActualPosition(SkeletonJoint joint, NISelectedPlayer player, SkeletonJointPosition torsoPos) { Vector3 actualPos; SkeletonJointTransformation trans; if (player.GetSkeletonJoint(joint, out trans) == false || trans.Position.Confidence <= 0.5) { Debug.Log("No actual position for Joint: " + joint); actualPos = ERROR_VECTOR; } actualPos = CalcJointPosition(joint, ref trans, ref torsoPos); return(actualPos); }
public bool IsSkeletonAvailable() { if (m_playerManager == null || m_playerManager.Valid == false) { return(false); // we can do nothing. } NISelectedPlayer player = m_playerManager.GetPlayer(m_playerNumber); if (player == null || player.Valid == false || player.Tracking == false) { return(false); } return(true); }
// protected methods /// this method tries to fill a new point on each of the relevant joints. /// It returns true if it succeed and false otherwise /// @note it will fail if even one of the points has a low confidence! /// @return true on success, false on failure. protected bool FillPoints() { // first we find a reference to the skeleton capability NISkeletonTracker hand = m_pointTracker as NISkeletonTracker; if (hand == null) { return(false); // no hand to track } NISelectedPlayer player = hand.GetTrackedPlayer(); if (player == null || player.Valid == false || player.Tracking == false) { return(false); // no player to work with... } // We need to figure out if we have a good confidence on all joints SkeletonJointPosition rightHand; SkeletonJointPosition leftHand; SkeletonJointPosition rightElbow; SkeletonJointPosition leftElbow; if (player.GetSkeletonJointPosition(SkeletonJoint.RightHand, out rightHand) == false || rightHand.Confidence <= 0.5f) { return(false); } if (player.GetSkeletonJointPosition(SkeletonJoint.LeftHand, out leftHand) == false || leftHand.Confidence <= 0.5f) { return(false); } if (player.GetSkeletonJointPosition(SkeletonJoint.RightElbow, out rightElbow) == false || rightElbow.Confidence <= 0.5f) { return(false); } if (player.GetSkeletonJointPosition(SkeletonJoint.LeftElbow, out leftElbow) == false || leftElbow.Confidence <= 0.5f) { return(false); } Vector3 pos = NIConvertCoordinates.ConvertPos(rightHand.Position); m_pointsRightHand.AddPoint(ref pos); pos = NIConvertCoordinates.ConvertPos(leftHand.Position); m_pointsLeftHand.AddPoint(ref pos); pos = NIConvertCoordinates.ConvertPos(rightElbow.Position); m_pointsRightElbow.AddPoint(ref pos); pos = NIConvertCoordinates.ConvertPos(leftElbow.Position); m_pointsLeftElbow.AddPoint(ref pos); return(true); }
/// Release the gesture public override void ReleaseGesture() { if (m_context != null && m_context.UserSkeletonValid) { NISkeletonTracker tracker = m_pointTracker as NISkeletonTracker; if (tracker.Valid) { NISelectedPlayer player = tracker.GetTrackedPlayer(); if (player != null && player.Valid && validRequestedPoseDetection) { m_context.UserGenrator.ReleasePoseDetection(m_poseName, player.OpenNIUserID); } } } m_context = null; m_poseName = ""; m_pointTracker = null; }
/// returns the current position with confidence /// /// @param confidence the confidence of the point /// @return the position public Vector3 GetPosWithConfidence(out float confidence) { confidence = 0.0f; NISelectedPlayer player = m_playerManager.GetPlayer(m_playerNum); if (player == null || player.Valid == false || player.Tracking == false) { return(Vector3.zero); // no position. } SkeletonJointPosition skelJointPos; if (player.GetSkeletonJointPosition(m_jointToUse, out skelJointPos) == false) { return(Vector3.zero); } confidence = skelJointPos.Confidence; return(NIConvertCoordinates.ConvertPos(skelJointPos.Position)); }
void DrawLineBetweenJoints(OpenNI.SkeletonJoint first, OpenNI.SkeletonJoint second) { NISelectedPlayer player = playerSelection.GetPlayer(0); OpenNI.SkeletonJointPosition firstJointPosition; player.GetSkeletonJointPosition(first, out firstJointPosition); OpenNI.SkeletonJointPosition secondJointPosition; player.GetSkeletonJointPosition(second, out secondJointPosition); if (firstJointPosition.Confidence <= 0.5 || secondJointPosition.Confidence <= 0.5) { return; } OpenNI.Point3D firstJointScreenPosition = depthGenerator.ConvertRealWorldToProjective(firstJointPosition.Position); OpenNI.Point3D secondJointScreenPosition = depthGenerator.ConvertRealWorldToProjective(secondJointPosition.Position); DrawLine.DrawSimpleLine(ref mapPixels, (int)(width - firstJointScreenPosition.X / factor), (int)(height - firstJointScreenPosition.Y / factor), (int)(width - secondJointScreenPosition.X / factor), (int)(height - secondJointScreenPosition.Y / factor), width, height, Color.white); }
/// This is true if the gesture is in the middle of doing (i.e. it has detected but not gone out of the gesture). /// for our purposes this means the steady event has occurred and the unsteady has not occurred yet /// @return a value between 0 and 1. 0 means no pose, 1 means the pose has been detected and held /// for a while. a value in the middle means the pose has been detected and has been held this /// portion of the time required to fire the trigger (@ref m_timeToHoldPose). public override float GestureInProgress() { if (m_context == null || m_context.UserSkeletonValid == false) { return(0.0f); // no data; } NISkeletonTracker tracker = m_pointTracker as NISkeletonTracker; if (tracker.Valid == false) { return(0.0f); // no one to track. } NISelectedPlayer player = tracker.GetTrackedPlayer(); if (player == null || player.Valid == false) { return(0.0f); } NIUserAndSkeleton.NIPoseDetectionStateStatus curStatus = new NIUserAndSkeleton.NIPoseDetectionStateStatus(); if (m_context.UserGenrator.GetPoseStatus(player.OpenNIUserID, m_poseName, ref curStatus) == false) { return(0.0f); // we do not have good pose information } if (curStatus.m_eState != PoseDetectionState.InPose) { return(0.0f); } if (curStatus.m_timePoseHeld < 0) { return(0.0f); } if (curStatus.m_timePoseHeld >= m_timeToHoldPose) { return(1.0f); } return(curStatus.m_timePoseHeld / m_timeToHoldPose); }
public void AdjustBone(SkeletonJoint joint1, SkeletonJoint joint2, NISelectedPlayer player, SkeletonJointPosition torsoPos) { //if(joint1) Vector3 joint1ActualPos = CalcActualPosition(joint1, player, torsoPos); Vector3 joint2ActualPos = CalcActualPosition(joint2, player, torsoPos); if (joint1ActualPos == ERROR_VECTOR || joint2ActualPos == ERROR_VECTOR) { return; } Transform joint1Trans = m_jointTransforms[(int)joint1]; Transform joint2Trans = m_jointTransforms[(int)joint2]; Vector3 joint1VirtualPos = joint1Trans.localPosition; Vector3 joint2VirtualPos = joint2Trans.localPosition; float actualDist = (joint1ActualPos - joint2ActualPos).magnitude; float virtualDist = (joint1VirtualPos - joint2VirtualPos).magnitude; float translateValue = actualDist - virtualDist; float translateFactor = translateValue / virtualDist; Vector3 translateVector = (joint1VirtualPos - joint2VirtualPos) * translateFactor; Debug.Log("Joint: " + joint1 + " is found! and translateVector = " + translateVector); Debug.Log("translateFactor = " + translateFactor + " = " + translateValue + "/" + virtualDist); //how to translate if (joint1 == SkeletonJoint.Waist || joint1 == SkeletonJoint.Neck)//translate Y axis up/down { joint1Trans.localPosition += translateVector; } else if (joint1 == SkeletonJoint.LeftShoulder || joint1 == SkeletonJoint.LeftHip) { //for left shoulder joint1Trans.localPosition += translateVector / 2; //for right shoulder : added joint2Trans.localPosition -= translateVector / 2; } else if (joint1 == SkeletonJoint.LeftElbow) //mirror! { //for left joint1Trans.localPosition += translateVector; //for right: added Transform rightElbowTrans = m_jointTransforms[(int)SkeletonJoint.RightElbow]; translateVector = (rightElbowTrans.localPosition - m_jointTransforms[(int)SkeletonJoint.RightShoulder].localPosition) * translateFactor; rightElbowTrans.localPosition += translateVector; } else if (joint1 == SkeletonJoint.LeftHand)//mirror! { joint1Trans.localPosition += translateVector; Transform rightHandTrans = m_jointTransforms[(int)SkeletonJoint.RightHand]; translateVector = (rightHandTrans.localPosition - m_jointTransforms[(int)SkeletonJoint.RightElbow].localPosition) * translateFactor; rightHandTrans.localPosition += translateVector; } else if (joint1 == SkeletonJoint.LeftKnee) { joint1Trans.localPosition += translateVector; Transform rightKneeTrans = m_jointTransforms[(int)SkeletonJoint.RightKnee]; translateVector = (rightKneeTrans.localPosition - m_jointTransforms[(int)SkeletonJoint.RightHip].localPosition) * translateFactor; rightKneeTrans.localPosition += translateVector; } else if (joint1 == SkeletonJoint.LeftFoot) { joint1Trans.localPosition += translateVector; Transform rightFootTrans = m_jointTransforms[(int)SkeletonJoint.RightFoot]; translateVector = (rightFootTrans.localPosition - m_jointTransforms[(int)SkeletonJoint.RightKnee].localPosition) * translateFactor; rightFootTrans.localPosition += translateVector; } }
/// @brief updates the skeleton /// /// This method is responsible for updating the skeleton based on new information. /// It is called by an external manager with the correct user which controls this specific skeleton. /// @param player The player object for the player controlling us. /// @param centerOffset the offset we should use on the center (when moving the root). /// This is usually the starting position (so the skeleton will not "jump" when doing the first update public void UpdateSkeleton(NISelectedPlayer player, Vector3 centerOffset) { if (player.Valid == false) return; // irrelevant player if (player.Tracking == false) return; // not tracking // we use the torso as root SkeletonJointTransformation skelTrans; if (player.GetSkeletonJoint(SkeletonJoint.Torso, out skelTrans) == false) { // we don't have joint information so we simply return... return; } UpdateRoot(skelTrans.Position, centerOffset); // update each joint with data from OpenNI foreach (SkeletonJoint joint in Enum.GetValues(typeof(SkeletonJoint))) { SkeletonJointTransformation skelTransJoint; if(player.GetSkeletonJoint(joint,out skelTransJoint) == false) continue; // irrelevant joint UpdateJoint(joint, skelTransJoint, skelTrans.Position); } }
/// @brief Internal method to initialize the players list. /// /// @note The players initialization is done during the mono behavior @ref Awake stage to enable /// calls to GetPlayer during the mono behavior start stage of @b other objects. protected virtual void InitPlayers() { // initialize the players to have an unselected user for each player. m_players = new List<NISelectedPlayer>(); for (int i = 0; i < m_MaxNumberOfPlayers; i++) { NISelectedPlayer player = new NISelectedPlayer(); player.User = null; m_players.Add(player); } }
/// @brief Method to change the number of players /// /// @note This method changes the player list. It will unselect all players and /// old player references might become invalid or irrelevant! This method also assumes that the /// player manager has already been initialized. /// @param newNum The new number of players /// @return True on success, false otherwise. public virtual bool ChangeNumberOfPlayers(int newNum) { if (newNum < 0) return false; if (newNum == m_MaxNumberOfPlayers) return true; // nothing to do... for(int i=0; i<m_players.Count; i++) { UnselectPlayer(i); } if (newNum < m_MaxNumberOfPlayers) { while (m_players.Count > newNum) { m_players.RemoveAt(m_players.Count - 1); } } else { while (m_players.Count < newNum) { NISelectedPlayer newPlayer = new NISelectedPlayer(); newPlayer.User = null; m_players.Add(newPlayer); } } m_MaxNumberOfPlayers = newNum; return true; // success }