Unity multiple Coroutines null reference exception - unity3d

I have seen the same question with an answer, but unfortunately I haven't fully understood how to address the issue, as I get a null reference exception.
I try to have 2 coroutines run simultaneously. Each is from a different base class. They both are identical, apart from the class's name and activation key (one is Up Arrow and the other Down Arrow).
public IEnumerator DetectArrow()
{
if (Input.GetKey(KeyCode.UpArrow))
{
if (PrintMessage == null)
{
PrintMessage += SendMessage;
}
}
return null;
}
public void SendMessage()
{
print("I am B.");
PrintMessage = null;
}
I try to run something such as this for the entire run (from a parent class):
void Update ()
{
if (b.PrintMessage != null)
{
b.PrintMessage();
}
if (a.PrintMessage != null)
{
a.PrintMessage();
}
}
So I'm not quite sure how to solve this. Thanks for the help.

Related

Unity & PhotonEngine. After scene reaload Joining to Lobby, CreateRoom and JoinRandom functions called repeatedly

I ran into a problem in the last step of a test project using Photon Network. When you first connect and join the room, everything goes without errors. However, after completing the match, exiting the room, and using LoadScene(), errors appear:
JoinLobby operation (229) not called because client is not connected or not yet ready, client state: JoiningLob <- in OnConnectedToMaster()
Through experience, I realized that the ConnectUsingSettings() methods and other Photon methods are called multiple times. But the connection to the lobby happens and I can create a room, but I immediately encounter MissingReferenceException errors.
I've seen a solution from guys who ran into this very same problem. The problems arose because of the events. Wherever this could happen, I unsubscribed from the events, but that doesn't help. What else can cause such problems, because I obviously missed something that prevents me from completely closing the scene during the transition?
Sorry for my language, used Google Translate
Code:
LobbyManager.cs
private void StartConnect()
{
PhotonNetwork.NickName = master.GameSettings.NickName;
PhotonNetwork.GameVersion = master.GameSettings.NickName;
PhotonNetwork.ConnectUsingSettings();
PhotonNetwork.AutomaticallySyncScene = true;
}
public override void OnConnectedToMaster()
{
Debug.Log("Connected to server");
if(!PhotonNetwork.InLobby) PhotonNetwork.JoinLobby();
}
public override void OnJoinedLobby()
{
onConnected.Invoke();//This use for show UIElements on Canvas
}
JoinRandomRoom class
public void OnClick_JoinRandomRoom()
{
if (!PhotonNetwork.IsConnected) return;
if (GameModeGlobalData.SelectedGameMode != null)
{
SetRoomOptions();
PhotonNetwork.JoinRandomRoom(expectedRoomProperties, GameModeGlobalData.SelectedGameMode.MaxPlayers);
}
}
public override void OnJoinRandomFailed(short returnCode, string message)
{
Debug.Log("Join random failed: " + message + ". Room will be created...");
_createRoomMenu.CreateAndJoinRoom();
}
public void SetRoomOptions()
{
expectedRoomProperties[RoomData.GAME_MODE] = GameModeGlobalData.SelectedGameMode.GameModeName;
}
private void OnDisable()
{
ShowPanels.RemoveAllListeners();
}
And CreateRoom.cs
private ExitGames.Client.Photon.Hashtable _roomCustomProperties = new ExitGames.Client.Photon.Hashtable();
public void CreateAndJoinRoom()
{
if (!PhotonNetwork.IsConnected) return;
if (GameModeGlobalData.SelectedGameMode != null)
{
RoomOptions roomOptions = GetCustomRoomOptions();
roomOptions.CleanupCacheOnLeave = true;
PhotonNetwork.CreateRoom(randomRoomName, roomOptions);
}
}
public RoomOptions GetCustomRoomOptions()
{
RoomOptions options = new RoomOptions();
options.MaxPlayers = _maxPlayer;
options.IsOpen = true;
options.IsVisible = true;
string[] roomProperties = new string[]{ RoomData.GAME_MODE };
_roomCustomProperties[RoomData.GAME_MODE] = GameModeGlobalData.SelectedGameMode.GameModeName;
options.CustomRoomPropertiesForLobby = roomProperties;
options.CustomRoomProperties = _roomCustomProperties;
return options;
}
The project has grown, and I blame myself for not testing it at the very beginning. Didn't think there would be problems at this stage
Sorry for this post. Its resolved. For those who may encounter this in the future, in addition to unsubscribing from events, check all classes that inherit from MonoBehaviourPunCallbacks for overridden OnDisable() methods.
Like this:
public override void OnDisable()
{
base.OnDisable();
}
This in turn will call the
PhotonNetwork.RemoveCallbackTarget(this);
Also, from the documentation:
Do not add new MonoBehaviour.OnEnable or MonoBehaviour.OnDisable. Instead, you should override those and call base.OnEnable and base.OnDisable.
I forgot about it and used MonoBehaviour.OnDisable.

Type already defines a member with the same parameter types

I have an error saying:
Type 'GameManager' already defines a member called 'Update' with the same parameter types
Error message in Unity
This is my code
Your code looks like this:
void Update() {
...
}
void Update() {
...
}
You can only have one Update() definition. They should be merged into one.
Ok. like others said what you're trying is using GameObject like a method.
And it's not gonna work that way.
The Error you're getting now is caused by duplicated Update().
Try Change first Update() to Start()
Edited my codes below.
try below codes
void Start()
{
SelectZombie(selectedZombie);
}
void Update()
{
if(Input.GetKeyDown("left"))
{
GetZombieLeft();
}
if(Input.GetKeyDown("Right"))
{
GetZombieRight();
}
if(Input.GetKeyDown("up")){
}
}
void GetZombieLeft()
{
if (selectedZombiePosition == 0)
{
SelectZombie(zombies[3]);
}
else
{
GameObject newZombie = zombies[selectedZombiePosition - 1];
SelectZombie(newZombie);
}
}
void GetZombieRight()
{
if (selectedZombiePosition == 3)
{
SelectZombie(zombies[0]);
}
else
{
SelectZombie(zombies[selectedZombiePosition + 1]);
}
}

Referencing to gameobject's renderer material after being instantiated from downloaded asset bundle not working

Hi! If you want to save time and still help please read this section and the last one to get glimpse of my problem (part 1 and 6). So much code was needed to fully present problem
Using Unity 2019.3.0b2
1.
Im creating WebGL application that allows you to customize your character via assets from downloaded asset bundles. So far I got downloading and instantiating work, but I also want to change downloaded gameobject material to custom color from color-picker. In this case I need to refer to adequate Renderer. I've got function that sends request and it goes like so:
private IEnumerator SendRequestCoroutine(UnityWebRequest request, UnityAction<UnityWebRequest> OnDownloadCompleteHandler, UnityAction<float> OnDownloadProgressHandler = null)
{
request.SendWebRequest();
while(!request.isDone)
{
if(OnDownloadProgressHandler != null)
OnDownloadProgressHandler.Invoke(request.downloadProgress);
yield return null;
}
// Has to fire once more because progress stops at around 0.87,
// never returning 1 unless download is finished.
if (OnDownloadProgressHandler != null)
OnDownloadProgressHandler.Invoke(1);
OnDownloadCompleteHandler.Invoke(request);
}
2.
I fire this coroutine like that:
public void DownloadAssetBundle(string url, ProgressBar bar = null)
{
if (isRequestSend)
{
Alerter.ShowMessage("Request has been already send, please wait untill complete.");
return;
}
UnityWebRequest request = HttpService.Instance.GetAssetBundleRequest(url);
if(bar != null)
{
HttpService.Instance.SendDownloadRequest
(
request,
(rq) => { OnDownloadAssetBundleCompleteHandler(rq); },
(rq) => OnDownloadProgressHandler(rq, bar)
);
isRequestSend = true;
}
else
{
HttpService.Instance.SendDownloadRequest
(
request,
(rq) => { OnDownloadAssetBundleCompleteHandler(rq); }
);
isRequestSend = true;
}
}
3.
OnDownloadAssetBundleCompleteHandler looks like this:
//Function that will handle asset bundle when completed.
private void OnDownloadAssetBundleCompleteHandler(UnityWebRequest request)
{
isRequestSend = false;
if(request.isHttpError || request.isNetworkError)
{
//Handle downloading error
Alerter.ShowMessage("Seems like there was a problem with downloading, try again.");
}
else
{
AssetBundle bundle;
//Handle content update
bundle = DownloadHandlerAssetBundle.GetContent(request);
AssetBundleInfo assetBundleInfo = bundle.LoadAllAssets<AssetBundleInfo>().FirstOrDefault();
if (assetBundleInfo == null)
{
//Handle error
Alerter.ShowMessage("Couldn't read information about this Character Part. AssetBundleInfo null exception.");
bundle.Unload(false);
return;
}
GameObject goToLoad = null;
goToLoad = bundle.LoadAsset<GameObject>(assetBundleInfo.ObjectName);
if (goToLoad == null)
{
Alerter.ShowMessage("Couldn't read information about this Character Part. Downloaded asset's gameobject null exception.");
bundle.Unload(false);
return;
}
Sticher.ConnectComponent(goToLoad, assetBundleInfo.PartType);
ColorSetter.Instance.ChangeSelectedBodyPart(assetBundleInfo.PartType);
bundle.Unload(false);
}
}
4.
Now the final step is to set adequate transform so my script will search for component of type Renderer, get its material of index 0 as current material to modify, class that contain ChangeSelectedBodyPart function looks like so:
using Assets.Scripts.Models.Enums;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ColorSetter : MonoBehaviour
{
public Renderer rend;
public ColorPicker picker;
public static ColorSetter Instance;
private void Awake()
{
if(Instance != null)
{
Destroy(this);
}
else
{
Instance = this;
DontDestroyOnLoad(this);
}
}
// Start is called before the first frame update
void Start()
{
picker.onValueChanged.AddListener(color =>
{
if (rend == null)
return;
rend.material.SetColor("_BaseColor", color);
}
);
}
public void ChangeSelectedBodyPart(AvatarPartType p)
{
switch(p)
{
case AvatarPartType.ClothesUpper:
SetActiveMaterial(Sticher.Instance.Clothes_Upper);
break;
case AvatarPartType.ClothesLower:
SetActiveMaterial(Sticher.Instance.Clothes_Lower);
break;
case AvatarPartType.Shoes:
SetActiveMaterial(Sticher.Instance.Shoes);
break;
case AvatarPartType.Hair:
SetActiveMaterial(Sticher.Instance.Hair);
break;
case AvatarPartType.Skin:
SetActiveMaterial(Sticher.Instance.Skin);
break;
}
}
private void SetActiveMaterial(Transform parent)
{
rend = parent.GetComponentInChildren<Renderer>();
}
}
5.
PS. parent has only one child that contains Renderer component
Now, finally, problem is that I don't get proper material reference, I got the old one that is being set via toggle button, simply as that:
public void OnValueChanged(bool value)
{
if(value)
{
ColorSetter.Instance.ChangeSelectedBodyPart(PartType);
ButtonManager.Instance.RemoveAllButtonsFromPartsWindow();
ButtonManager.Instance.PopulatePartsPanelWithAvatarPartsOfType(PartType);
}
}
6.
So in conclusion when I press on "toggle button" that represents some avatar body/clothing part it sets its parent and material properly via function, even after asset bundle has been downloaded (but I have to click the same toggle again to make it work), but when I fire the same function in OnDownloadAssetBundleCompleteHandler just after asset been downloaded it doesn't work :S Why? Is it related with asset unloading speed? Any tips on fixing this?
In Game View it behave like that:
I fixed it. Since you can't use DestoyImmediate like #derHugo said because it can cause reference errors or even it could destroy assets permamently I had to use Destroy() instead before Instantiating new gameobject from assetbundle, however Destroy() will delete given object at the end of the frame while I try to access Renderer component on freshly instantiated GameObject just at the same frame, before old one is acutally destoryed. I fixed it yielding one frame using yield return new WaitForEndOfFrame(); just after function that takes care of destroying old GameObjects before trying to access new GameObject.

IEventBroker subscription handles the same event more than once and handles incorrectly

I am bootstrapping the IEventBroker in a compat-layer Eclipse RCP app.
I have two views: Triggerer and Receiver.
Triggerer (excerpts):
private IEventBroker eventBroker = PlatformUI.getWorkbench().getService(IEventBroker.class);
btn.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
IStructuredSelection selection = viewer.getStructuredSelection();
List selectionList = selection.toList();
for (Object s : selectionList) {
if (s instanceof MyObject) {
matches.add(s);
}
}
eventBroker.send(MyEventConstants.TOPIC_OBJECT_CHANGED, matches);
}
}
Receiver (excerpts):
#Override
public void handleEvent(Event event) {
Object data = event.getProperty(EVENT_DATA);
switch (event.getTopic()) {
case MyEventConstants.TOPIC_OBJECT_CHANGED:
try {
if (data instanceof ArrayList) {
List<MyObject> matches = null;
try {
matches = (List<MyObject>) data;
}
catch (ClassCastException e) {
}
Subthing sub = buildSubthing(matches);
getContentViewer().getContents()
.setAll(Collections.singletonList(sub));
}
}
break;
}
}
buildSubthing does stuff with the respective received data, and sets it to the contents of a GEF4 editor.
In some cases this works just fine, in some it doesn't.
handleEvent() is triggered more than once, although the event hashCode is always the same, and I don't understand why. The topic is the same and the data is also the same. However, buildSubthing just stalls for no apprent reason with some data while it doesn't for other. The data is structurally the same in both cases.
How can I control how often handleEvent is called, as I think the number of times it's called is the reason while the Subthing is sometimes not correctly constructed?

Why Shiro's SubjectCallable need invoke restore method?

SubjectCallable's call method:
public V call() throws Exception {
try {
threadState.bind();
return doCall(this.callable);
} finally {
threadState.restore();
}
}
1.bind method is necsssary, but restore is why?
public void bind() {
SecurityManager securityManager = this.securityManager;
if ( securityManager == null ) {
//try just in case the constructor didn't find one at the time:
securityManager = ThreadContext.getSecurityManager();
}
this.originalResources = ThreadContext.getResources();
ThreadContext.remove();
ThreadContext.bind(this.subject);
if (securityManager != null) {
ThreadContext.bind(securityManager);
}
}
public void restore() {
ThreadContext.remove();
if (!CollectionUtils.isEmpty(this.originalResources)) {
ThreadContext.setResources(this.originalResources);
}
}
2.originalResources is use to do ? each time enter the AbstractShiroFilter will create a new subject and invoke it's execute method, the originalResources seems useless.
General thread health. You need to clean up resource in case the thread is re-used (very common). And it would help with garbage collection too.
Do you ever go hiking? Leave no trace ;)