Such an idea, for which I don't even have any ideas how to do it right. In general, the thought is:
There are, say, three NPCs on the stage (Conditionally), and there is also one building (Conditionally). Each NPC has its own task, but I do not know how to take an NPC who does not have a task and send one NPC to work in the building, and not several at once. Namely, each NPC has work statuses, and there is also a status 0, meaning that the NPC is not busy, there may be several such "free" ones, and if one is assigned the status of work - go to the building, then everyone who has this status will go, but how can we make sure that only one free NPC is taken and sent there?
I know the question is as incomprehensible and terrible as possible, but maybe someone will understand...
I made, so, to begin with, the code:
File WorkingPlace:
public static bool SignalFreeNPCPoint = false;
public static int WorkingPlaceNPC = 0;
public void ProductBoardNPC()
{
SignalFreeNPCPoint = true;
WorkingPlaceNPC += 1;
}
public void ProductBoard_Stop()
{
WorkingPlaceNPC -= 1;
SignalFreeNPCPoint = true;
}
This "ProductBoardNPC()" and "ProductBoard_Stop()" method's for button in Unity.
File NPCSctipt:
public List<GameObject> ListGM = new List<GameObject>();
public int TaskNumber = 0;
void Update()
{
TaskManager();
LogicNPC();
}
private void TaskManager()
{
switch (TaskNumber)
{
case 0:
//You code
break;
case 1:
ListGM.Remove(gameObject);
break;
case 2:
ListGM.Remove(gameObject);
break;
case 3:
ListGM.Remove(gameObject);
break;
case 4:
ListGM.Remove(gameObject);
break;
}
}
private void LogicNPC()
{
if (WorkingPlace.SignalFreeNPCPoint == true)
{
if (TaskNumber == 0 & WorkingPlace.WorkingPlaceNPC == 1)
{
ListGM.Add(gameObject);
TaskNumber = 4;
}
if (TaskNumber == 4 & WorkingPlace.WorkingPlaceNPC == 0)
{
TaskNumber = 0;
}
WorkingPlace.SignalFreeNPCPoint = false;
}
}
How the code works:
When you click on the button, an NPC with the status 0 is searched, the last NPC with this status is taken and entered into the array, after which it is given a different status to perform "work", after which it is removed from the array to make room for another NPC.
In this case, the array is a buffer and is simply necessary. If you have better ideas than mine, I will be happy to listen)
The implementation is a clean bike made of crutches, but it works flawlessly. Yes, about FPS drawdowns, it doesn't seem to load the system much, if there are drawdowns, then write what to fix.
Related
Hi Guys I am converting a single player Sudoko game into a multiplayer game (right now 2 players) using Photon in Unity.
The basic logic of the Sudoku game is that there are 300 puzzle data already loaded in it. A random number between 1 to 300 is picked up and the corresponding puzzle is loaded.
But the problem I am facing is even though I have made sure that the same number is getting picked up for both the client and the master server, different puzzles are getting loaded.
So basically I script called MultiManager attached to the MultiManager GameObject in the Sudoku screen.The script looks something like this.
void Start()
{
PV = GetComponent<PhotonView>();
if (PhotonNetwork.IsMasterClient)
{
puzzleIndex = Random.Range(0, 300);
PV.RPC("RPC_PuzzleIndex", RpcTarget.Others, puzzleIndex);
}
gameManager = FindObjectOfType(typeof(GameManager)) as GameManager;
gameManager.PlayNewGame("easy");
}
[PunRPC]
void RPC_PuzzleIndex(int puzzleIndexNUmber)
{
puzzleIndex = puzzleIndexNUmber;
}
So in the GameManager script you have these functions:
public void PlayNewGame(string groupId)
{
// Get the PuzzleGroupData for the given groupId
for (int i = 0; i < puzzleGroups.Count; i++)
{
PuzzleGroupData puzzleGroupData = puzzleGroups[i];
if (groupId == puzzleGroupData.groupId)
{
PlayNewGame(puzzleGroupData);
return;
}
}
}
private void PlayNewGame(PuzzleGroupData puzzleGroupData)
{
// Get a puzzle that has not yet been played by the user
PuzzleData puzzleData = puzzleGroupData.GetPuzzle();
// Play the game using the new puzzle data
PlayGame(puzzleData);
}
And in the PuzzleGroupData class you have this function :
public PuzzleData GetPuzzle()
{
return new PuzzleData(puzzleFiles[MultiManager.puzzleIndex], shiftAmount, groupId);
}
I don't quite get as to whats wrong which is happening. I tried to use other variations like keeping that random number outside of the condition inside of PhotonNetwork.isMasterClient and all, but doesn't work.
If anyone can help it would be great. By the way this Sudoku game was purchased and I am trying to convert it to a mutliPlayer game
Since the master is usually the first one in a room so also the first one getting Start called I think what happens is that your other clients are simply not connected yet when the RPC is called.
Further it might also happen (actually pretty likely) that Start is called before the RPC has the chance to be received.
I would rather actually wait until you have the value and do
void Start()
{
PV = GetComponent<PhotonView>();
if (PhotonNetwork.IsMasterClient)
{
PV.RPC(name of(RPC_PuzzleIndex), RpcTarget.AllBuffered, Random.Range(0, 300));
}
}
[PunRPC]
void RPC_PuzzleIndex(int puzzleIndexNUmber)
{
puzzleIndex = puzzleIndexNUmber;
gameManager = FindObjectOfType(typeof(GameManager)) as GameManager;
gameManager.PlayNewGame("easy");
}
This way
the RpcTarget.AllBuffered makes sure that also clients joining later will receive the call
there is no way you start a game without receiving the random value first
i have a problem. iam making a game that have button for buy item , and the button have no issue, it run normally, but the problem is the button immediately executes the method without seeing that the method contains an if statement, so what is wrong here Here's the code :
public void TakeMoney()
{
int minusCoin = PlayerInfo.coin -= 5;
PlayerPrefs.SetInt("MyCoinAmount", minusCoin);
}
public void DisableBuyButton()
{
disableBuyButtonRocket3 = 1;
PlayerPrefs.SetInt("DisableBuyButtonRocket3", disableBuyButtonRocket3);
}
public void Buy()
{
int getPlayerCoin = PlayerInfo.coin;
if (getPlayerCoin > 4)
{
if (MyRocket._rocket3 == 1)
{
GetComponent<AudioSource>().Play();
MyRocket._rocket3 = 1;
PlayerPrefs.SetInt("Rocket3Bought", MyRocket._rocket3);
}
}
}
so the button will perform all of the 3 functions above, but as can be seen that I make an if statement so the item can only be purchased if we already have enough money, but when I try I try to reset all values, so my money by default it is 0, but I can buy the item and my money becomes a minus.
Btw, PlayerInfo above is a script from another scene.
Sorry for my bad english.
Your problem is that you do everything in separated methods. It's basically not a problem - but, when you execute them concurrently, it is. Also, your methods are completely independent of each other - an if statement in one will not affect the others' execution. The solution is that you first remove the event listeners to TakeMoney and DisableBuyButton. Then, call them from Buy. If you think it through, it is more logical. Also, remove the temporary variables, as they slow down your code a bit and also make it less readable. Final code could be something like this:
public void TakeMoney()
{
PlayerInfo.coin -= 5;
PlayerPrefs.SetInt("MyCoinAmount", PlayerInfo.coin);
}
public void DisableBuyButton()
{
PlayerPrefs.SetInt("DisableBuyButtonRocket3", 1);
}
public void Buy()
{
if (PlayerInfo.coin > 4) {
TakeMoney();
DisableBuyButton();
if (MyRocket._rocket3 != 1 /* changed this, because it seemed that you want to check whether it is NOT one */)
{
GetComponent<AudioSource>().Play();
// I changed the if statement because this
MyRocket._rocket3 = 1;
PlayerPrefs.SetInt("Rocket3Bought", MyRocket._rocket3);
}
}
}
the playerinfo.coin value would be more than 4 because it will decrease coin at click so it will minus only 5 at a click you could check playerinfo.coin value
I am trying out Area Recognition using Area Learning with predefined ADF files using Project Tango in Unity3d. I use the script from this tutorial as the basis, but for some reason it won't relocalize.
using UnityEngine;
using System.Collections;
using Tango;
public class TestADFFile : MonoBehaviour, ITangoLifecycle
{
private TangoApplication m_tangoApplication;
public UnityEngine.UI.Text statusText;
public string adfName;
public void Start()
{
m_tangoApplication = FindObjectOfType<TangoApplication>();
if (m_tangoApplication != null)
{
m_tangoApplication.Register(this);
m_tangoApplication.RequestPermissions();
}
}
public void OnTangoPermissions(bool permissionsGranted)
{
if (permissionsGranted)
{
if(AreaDescription.ImportFromFile(System.IO.Path.Combine(Application.streamingAssetsPath, "adfs/" + adfName))){
statusText.text = "success!";
}
else{
statusText.text = "fail!";
}
AreaDescription[] list = AreaDescription.GetList();
AreaDescription mostRecent = null;
AreaDescription.Metadata mostRecentMetadata = null;
if (list.Length > 0)
{
// Find and load the most recent Area Description
mostRecent = list[0];
mostRecentMetadata = mostRecent.GetMetadata();
foreach (AreaDescription areaDescription in list)
{
AreaDescription.Metadata metadata = areaDescription.GetMetadata();
if (metadata.m_dateTime > mostRecentMetadata.m_dateTime)
{
mostRecent = areaDescription;
mostRecentMetadata = metadata;
}
}
m_tangoApplication.Startup(mostRecent);
}
else
{
// No Area Descriptions available.
Debug.Log("No area descriptions available.");
}
}
}
public void OnTangoServiceConnected()
{
}
public void OnTangoServiceDisconnected()
{
}
}
The statusText is set to "success" so apparently the ADF is successfully loaded, right?
You're doing it right. Relocalization with area learning mode off works very quick. But relocalization with area learning mode on takes quite a while. You have to walk around up to 3-5 minutes until the relocalization works. Don't give up. I had the same problem. But in the end, it works!
Is not an answer I think, but just a checklist of things I would check.
Check that the ADF to relocalize is the right one, you may be loading an ADF from a different area.
On the TangoManager component check if Enable Area Descriptions is set to true.
Also check if the scene is set up correctly with all tango components required like: RelocalizingOverlay.cs, TangoApplication.cs
Also as a personal note I have noticed that ADF sometimes take quite a while to relocalized and sometimes they don't relocalize at all.
I think the reasons are that the room has change in someway or the lighting is very different, by different I mean maybe the day you recorded that ADF was a very sunny day and the day you are trying to relocalize is cloudy.(Again this are just theories of mine, I got from my testing)
I have looped through some game objects to set the isTrigger property before a certain collision occurs but though the property is true collision with the object still occurs. Please see the relevant code below:
void OnCollisionEnter2D(Collision2D col) {
if (col.gameObject.tag == "object1") {
for (int i = 0; i < badGuys.Count; i++) {
badGuys[i].getBadGuyGameObject().GetComponent<Collider2D>().isTrigger = true;
}
}
else if (col.gameObject.tag == "object2") {
// collision with object1 always occurs before object2, though isTrigger is true the colliding object doesn't pass through the badGuys game objects, it bounces off on collision
}
else if (col.gameObject.tag == "object3") {
for (int i = 0; i < badGuys.Count; i++) {
badGuys[i].getBadGuyGameObject().GetComponent<Collider2D>().isTrigger = false;
}
}
else if (col.gameObject.tag == "badGuy") {
}
}
collision with object1 always occurs before object2, though isTrigger is true the colliding object doesn't pass through the badGuys game objects, it bounces off on collision. How can I solve this?
UPDATE 1
I just realized that the isTrigger value is true or false in the inspector depending on the time I stop the game. For example if I stop the game after Object1 collision the isTrigger is true and when I stop the game after collision with object3 it false. This inconsistency makes debugging very cumbersome. Is this a bug or something?
UPDATE 2
Based on recommendation from Joe Blow and Agustin0987 I used the enabled property of Collider2D instead of isTrigger. I also removed virtually all the code in OnCollisionEnter2D to get a simple test scenario. Please see code below:
void OnCollisionEnter2D(Collision2D col) {
if (col.gameObject.tag == "object1") {
if (badGuys[4].getBadGuyGameObject().GetComponent<Collider2D>().enabled = false) {
badGuys[4].getBadGuyGameObject().GetComponent<Collider2D>().enabled = true;
Debug.Log ("CHANGED TO TRUE");
Debug.Log ("bad guy collider enabled is " + badGuys[4].getBadGuyGameObject().GetComponent<Collider2D>().enabled);
}
else if (badGuys[4].getBadGuyGameObject().GetComponent<Collider2D>().enabled = true) {
badGuys[4].getBadGuyGameObject().GetComponent<Collider2D>().enabled = false;
Debug.Log ("CHANGED TO FALSE");
Debug.Log ("bad guy collider enabled is " + badGuys[4].getBadGuyGameObject().GetComponent<Collider2D>().enabled);
}
}
else if (col.gameObject.tag == "badGuy") {
Debug.Log("collided with bad guy"); // DOESN'T OCCUR
}
}
Instead of looping through this time, I decided to test for one. The else if is where enabled is true always satisfied though the Log prints false and the if where enabled is false is never satisfied. In the inspector, the Box Collider2D for bad guys is always unchecked. Collision with the badGuy never occurs. Based on my simple scenario code I thought it should be working.
Never, ever ever
I mean absolutely never
use "else if".
Simply, never, ever - ever - type "else if" for the rest of your life, until you die.
In the first instance, delete all your code and replace it with this:
void OnCollisionEnter2D(Collision2D col)
{
Debug.Log("We will deal with this ......... " +col.gameObject.name);
if (col.gameObject.tag == "object1")
{
Debug.Log("\t handling 'object' type issue");
HandleObjectIssue(col)
return; //NOTE HERE
}
if (col.gameObject.tag == "object1")
{
Debug.Log("\t handling 'bad guy' type issue");
HandleBadGuyIssue(col)
return; //NOTE HERE
}
}
private void HandleObjectIssue(Collision2D col)
{
Debug.Log("'\t\t object', I'm dealing with this .. " +col.gameObject.name);
}
private void HandleBadGuyIssue(Collision2D col)
{
Debug.Log("\t\t 'badguy', I'm dealing with this .. " +col.gameObject.name);
}
Run that extensively and "unit test" it. If you like, convert back (USING THAT CODE) to a "loop" method to look at all items. In any event, test that extensively.
--
Secondly, add this routine ..
private void ToggleColliderOnGameObject( GameObject gg )
{
Debug.Log("I'm supposed to toggle the collider on: " +gg.name);
Collider2D { or whatever } col = gg.GetComponent<Collider2D>();
bool currentState = GetComponent<Collider2D>().enabled;
Debug.Log("\t it is currently: " +currentState);
bool newState = ! currentState;
col.enabled = newState;
Debug.Log("\t\t but now it is: " +newState);
}
... and start unit testing with it. So you will have code like:
ToggleColliderOnGameObject( badGuys[4].getBadGuyGameObject() );
For example, try some things like this:
void Start()
{
InvokeRepeating("teste", 1f, 1f);
}
private void Teste()
{
ToggleColliderOnGameObject( badGuys[4].getBadGuyGameObject() );
}
After experimenting with that for some time and unit testing, you will be able to move on.
Note that apart from other problems, it is likely that (for example) your routine "getBadGuyGameObject" is returning the wrong thing - or some similar problem.
EDIT:
Remove badGuy = badGuyPrefab; from your public void createBadGuy(GameObject badGuyPrefab, BadGuyType badGuyType, Vector3 badGuyPosition) function. This was a mistake in Programmer's code from the link in your comment.
It seems like you are trying to enable/detect the collision between badGuys and object2 only if object1 collides with whatever object has this script attached (seems to be some kind of BadGuyManager). So I would recommend that instead of setting isTrigger to true or false, you should try to enable or disable the whole `Collider2D.
P.S: I agree with Joe Blow in that you should explain what are you trying to achieve.
I am trying to make a game in where you can stab an enemy, the enemy struggles for about a second and drops dead. (ragdoll);
i think its best to just show my script and you know what I mean:
In an on trigger enter script:
if(other.tag == "enemy"){
other.transform.parent.gameObject.name = ("enemy" + currentEnemy);
print(other.name);
gameObject.Find("enemy" + currentEnemy).GetComponent("RagdollOrNot").MakeKinematicFalse();
BloodParticle.emit = true;
Stabbed = true;
Character.GetComponent("MouseLook").enabled = false;
Character.GetComponent("CharacterMotor").enabled = false;
}
and in the update function:
if(Stabbed == true){
StopBleeding ++;
}
if(StopBleeding > 50){
Stabbed = false;
StopBleeding = 0;
currentEnemy ++;
Character.GetComponent("MouseLook").enabled = true;
Character.GetComponent("CharacterMotor").enabled = true;
BloodParticle.emit = false;
}
Now when my knife enters the collision of the enemy the enemy imediatly drops to the floor.
I tried putting the:
gameObject.Find("enemy" + currentEnemy).GetComponent("RagdollOrNot").MakeKinematicFalse();
in the update function in if(StopBleeding > 50).
if I do that I get an error of Null reverance exception becaus the script cand find the enemy. While it can I its in the trigger enter.
Basicly my question is: Is there any way to fix this error to give it a 50 frame delay (all the rest in StopBleeding works)?
Or is there any way I can put a simple delay in before the Ragdoll gets activated?
Thanks in advance
You can use StartCoroutine("DelayDeath"); where DelayDeath is the name of a method. See below.
if(other.tag == "enemy"){
other.transform.parent.gameObject.name = ("enemy" + currentEnemy);
print(other.name);
StartCoroutine("DelayDeath");
BloodParticle.emit = true;
Stabbed = true;
Character.GetComponent("MouseLook").enabled = false;
Character.GetComponent("CharacterMotor").enabled = false;
}
private IEnumerator DelayDeath()
{
//this will return...and one second later re-enter and finish the method
yield return new WaitForSeconds(1.0f);
gameObject.Find("enemy" + currentEnemy).GetComponent("RagdollOrNot").MakeKinematicFalse();
}
Since you're asking specifically about Unity JS and not C#, you'll use the yield keyword.
print("foo");
yield WaitForSeconds (5.0); // waits for 5 seconds before executing the rest of the code
print("bar");
On stabbing you could look up the enemy object but save it away in a variable instead of issuing the enemies dead. After the bleeding finishes you don't have to look up the enemy and can trigger the death on the saved one.
Another hint: Don't count frames as frames might be of different length. Add up time from Time.deltaTime until you reach let's say 2 seconds and then trigger the death.
Hope that helps.