How to make an interactive objects using PhotonNetwork? - c#

On the stage, I create several objects using PhotonNetwork.Instatiate ();
On these objects there is a script that changes the transform of the object. In the same script, a random variable is selected from the list, depending on which the transform is changing.
Now the question. How do I make this object change its transform simultaneously and equally for all players?
I'm thinking of doing something like this: make a separate script that will select a random number -> in it, write something like this
if (view.isMaster) // ???????
for (int i = 0; ......) {
objectInList [i] .myFloatValue = randomValueInArray;
objectInList [i] .canToChange = true;
}
And in the script of a mutable object, write something like:
if (canToChange)
// change the position;

Related

Passing the cycle from the beginning to the end (Or vice versa)

Good afternoon guys, such a question, I'm doing an array with brute force. I can't understand why assigning a value from the last element of the FrezeTree array to the BufferObject works in the Start() method, but this does not happen in Update.
So.
At the start of the scene, there is a search for all objects with the Tree tag (This is a temporary and debag solution):
FrezeTree = GameObject.FindGameObjectsWithTag("Tree");
Then, this list is passed to the array:
public GameObject[] FrezeTree;
Next, I'm trying to pass one element from the array to another object (This happens in the Start() method):
foreach (GameObject gm in FrezeTree)
{
BufferObject = gm;
}
But it turns out that only the last element is transmitted (Because of My own crookedness, since I don't know how to fix it yet). Why do I need a Buffer Object? From it I get the X position of the object, which I use for other purposes. The idea is to transfer one element from the array to the object, and when the object ceases to exist (Gets the null status), it goes to the element above / below (No matter in what order). Yes, I know that in what I have given above and I do not feel that I am trying to make a transition or skip an element from the array. I found various solutions on the great Internet, but the result was always the same, gets the last element and does not choose another one. That's why I turned here.
As I understand you want to
set one closest target object
wait until it is destroyed
go to the next closest target object
You could do this by simply checking whether the BufferObject is already set and still alive like e.g.
using System.Linq;
...
public GameObject BufferObject;
private void Update ()
{
// This means BufferObject is either not assigned yet or was destroyed
if(!BufferObject)
{
BufferObject = FindClosestTarget();
}
}
private GameObject FindClosestTarget ()
{
// Get all currently existing trees
// since this happens only once in a while it should be okey to repeat this call
return GameObject.FindGameObjectsWithTag("Tree")
// order them by distance
.OrderBy(gm => (transform.position - gm.transform.position).sqrMagnitude)
// get the first one or null if there was none
.FirstOrDefault();
}
This way it would also take trees that are spawned later into account.
If you rather want to cache this array only once you can still do this like
private GameObject [] trees;
public GameObject BufferObject;
private void Start ()
{
trees = GameObject.FindGameObjectsWithTag("Tree");
}
private void Update ()
{
// This means BufferObject is either not assigned yet or was destroyed
if(!BufferObject)
{
BufferObject = FindClosestTarget();
}
}
private GameObject FindClosestTarget ()
{
// First filter out any null or destroyed objects
return trees.Where(t => t)
// Then order the rest by distance
.OrderBy(gm => (transform.position - gm.transform.position).sqrMagnitude)
// Then pick the first one or null if there isn't any
.FirstOrDefault();
}
See
Linq Where
Linq OrderBy
Linq FirstOrDefault
implicit UnityEngine.Object.bool operator
In general, I figured out how to implement everything myself. So, the code:
public GameObject[] FrezeTree; //Array of all objects in the scene
private void Start()
{
FrezeTree = GameObject.FindGameObjectsWithTag("Tree"); //Finding all objects with a tag (I do not recommend using tags due to the lack of code navigation)
}
private GameObject FindClosestTarget()
{
return False True.Where(t => t).FirstOrDefault(); //Assigning and sorting an array with objects from the scene, thanks #derHugo
}
void Update()
{
BufferObject = FindClosestTarget(); //will be Assigned the next element of the array, in case the current will become "null"
if (BufferObject == null)
{
Debug.Log("debug for (BufferObject == null)"); //arbitrary code in the case that any object on the stage left.
}
}
P.S. This code is perfect for creating a simple AI for NPCs in your game. It's not perfect, but at least something is better than nothing :) Thanks #derHugo

Set a variable within a Unity class

I'm currently following a brackeys tutorial on dialogue, however I want to import multiple names, as well as multiple lines of dialogue along with them, through a text file rather than handwriting them. This differs greatly from the tutorial, so I'm stuck on creating a Dialogue class. Here's what I wrote up:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Dialogue {
// text file input
public TextAsset textFile;
// list containing each line of the text file
protected string[] textLines;
// lists containing names and dialogues (written in the file like so: Bob;I like pancakes)
protected string[] names;
protected string[] dialogues;
// splits up the text file into the list of lines
textLines = textFile.text.Split('\n');
// iterates over the course of the list of lines, splitting up the names and dialogue, then sorting each into its respective list
for (int i = 0; i <= textLines.Length - 1; i++) {
names.Add(textLines[i].Split(';')[0]);
dialogue.Add(textLines[i].Split(';')[1]);
}
}
Unity, however, does not like this, and spits out loads of errors such as:
Assets\scripts\Dialogue.cs(12,15): error CS1519: Invalid token '=' in class, struct, or interface member declaration
and
Assets\scripts\Dialogue.cs(12,36): error CS1519: Invalid token '(' in class, struct, or interface member declaration
among others.
I assume there are limits when working with unity classes such as this one, however I evidently don't know them, and after looking through the internet, I was unable to find them. Any help with this is appreciated!
NOTE: I have never used Unity before, so take this with a grain of salt.
You can only assign class fields when you declare them, and you can only place for loops inside functions and properties (get and set are technically functions). Though class fields ARE modified in the application's lifetime, so there is no need to worry.
Also, string[]s do not have the .Add() method in them. Either convert the array into a List<string>, do an expensive operation that pushes new elements into an array or define a fixed size for the array.
You can do this:
/**
* You can do one of these:
* protected List<string> names; (we will use this)
* protected string[] names = new string[SOME_SIZE];
* protected string[] names; (and push elements)
*/
void AssignTextLines() {
// splits up the text file into the list of lines
textLines = textFile.text.Split('\n');
// iterates over the course of the list of lines, splitting up the names and dialogue, then sorting each into its respective list
for (int i = 0; i < textLines.Length; /* you don't need -1! */ i++) {
names.Add(textLines[i].Split(';')[0]); // only for List<T>!!
dialogues.Add(textLines[i].Split(';')[1]);
}
}
or this:
protected string[] textLines = textFile.text.Split('\n');
void Something() {
// then do the thing above
for (int i = 0; i < textLines.Length; i++) {
names.Add(textLines[i].Split(';')[0]); // only for List<T>!!
dialogues.Add(textLines[i].Split(';')[1]);
}
}
You can also replace the for loop with a foreach loop.
foreach (string txt in textLines) {
names.Add(txt.Split(';')[0]); // only for List<T>!!
dialogues.Add(txt.Split(';')[1]);
}
The error in your code is that you have written these:
// splits up the text file into the list of lines
textLines = textFile.text.Split('\n');
// iterates over the course of the list of lines, splitting up the names and dialogue, then sorting each into its respective list
for (int i = 0; i <= textLines.Length - 1; i++) {
names.Add(textLines[i].Split(';')[0]);
dialogue.Add(textLines[i].Split(';')[1]);
}
Inside the class instead of a method inside the class.
Coming to the actual problem, I think you should use a ScriptableObject in this case. They provide an Awake (Similar to Awake in MonoBehavior) method in which you can write the above code so that it gets executed when the game starts.
[CreateAssetMenu(fileName="Dialogue Asset", menuName="My Game/Dialogue Asset")]
public class Dialogue: ScriptableObject {
// text file input
public TextAsset textFile;
// list containing each line of the text file
protected string[] textLines;
// lists containing names and dialogues (written in the file like so: Bob;I like pancakes)
protected string[] names;
protected string[] dialogues;
void Awake() {
textLines = textFile.text.Split('\n');
for (int i = 0; i <= textLines.Length - 1; i++) {
names.Add(textLines[i].Split(';')[0]);
dialogue.Add(textLines[i].Split(';')[1]);
}
}
}
Now you can access the textLines and other things when the game is running. To create a new dialogue asset, you can go to Asset/My Game/Dialogue Asset from the menu bar in Unity and Edit them in the inspector. This is just a brief overview of ScriptableObject class. I suggest you watch Scriptable Objects - Brackey's to get the concept of scriptable objects better.

How to replace element in MeshRenderer.sharedMaterials

We are using a object from the asset store that makes usage's of a mesh renderer with 32 materials in it. We need to implement functionality that makes it possible to replace 1 single material in the list, and keep the rest.
I have tried several things, but I keep struggeling with the materials list, that is why I decided to ask you guys for help.
public Material TestMaterial;
void Update()
{
for (int i = 0; i < meshRenderer.sharedMaterials.Length; i++)
{
if (meshRenderer.sharedMaterials[i].name == "replaceableMat")
{
// Replace with TestMaterial
}
}
}
The above code is how I kinda want to use it.
The TestMaterial object is not null, it is selected from the Unity Editor, so that is fine.
Could someone give me some insight?
Thanks in forward!
from Renderer.sharedMaterials:
Note that like all arrays returned by Unity, this returns a copy of materials array. If you want to change some materials in it, get the value, change an entry and set materials back.
You have to do exactly that. Store the materials in a temporary variable, change entries and write the entire array back to sharedMaterials when done:
var materials = meshRenderer.sharedMaterials;
for(int i = 0; i < materials.Length; i++)
{
if(!string.Equals(materials[i].name, "replaceableMat") continue;
materials[i] = TestMaterial;
}
meshRenderer.sharedMaterials = materials;
The reason in the background is that sharedMaterials is not a field but a property.
Since the assignment of sharedMaterial[i] is not an assignment to the whole property what it does is just replacing that entry in a temporary array but not actually assigning it back to the Renderer component.
Only by assigning a value to the entire property actually makes the property execute it's setter and writes the array back to the Renderer component.
This should work.
void Update()
{
for (int i = 0; i < meshRenderer.sharedMaterials.Length; i++)
{
if (meshRenderer.sharedMaterials[i].name == "replaceableMat")
{
// Replace with TestMaterial
renderer.sharedMaterials[i] = TestMaterial
}
}
}

C# & Unity : Pass reference by value?

I'm new to C# and Unity, and here I am tweaking and creating my first minigame.
Here is the problem:
I've got a little cube, that moves. I've implemented a method that checks the next position before making a move.
The method receives as parameters the current cube position, and the direction:
public bool okToMove(Transform playerCurrentPosition , int directionIndex)
{
Transform playerNextPosition = playerCurrentPosition;
playerNextPosition.Translate(toDirection(directionIndex));
if (playerNextPosition.position.x > 1 ||
playerNextPosition.position.x < -1 ||
playerNextPosition.position.y > 1 ||
playerNextPosition.position.y < -1)
return false;
else
return true;
}
Then, I call the method
public void movePlayer(int directionIndex)
{
if ( okToMove(gameObject.transform, directionIndex) )
transform.Translate(toDirection(directionIndex));
}
The problem is that the cube makes 2 moves at once. This is because of
transform.Translate(toDirection(directionIndex));
and
playerNextPosition.Translate(toDirection(directionIndex));
that is called from okToMove method. Unity or C# sees playerNextPosition as the real cube, and not somekind of temporary copy that only exists inside the method.
So why is my gameObject.transform being passed as a reference and not by value? How can I make it work?
Thanks in advance and sorry for my noobiness.
You are passing reference to Transform and then moving it with translate in "okToMove", best way is to make a copy of Vector3, just change your "okToMove" like this.
public bool okToMove(Transform playerCurrentPosition , int directionIndex){
Vector3 playerNextPosition = playerCurrentPosition.position;
playerNextPosition += toDirection(directionIndex);
if (playerNextPosition.x > 1 ||
playerNextPosition.x < -1 ||
playerNextPosition..y > 1 ||
playerNextPosition.position.y < -1)
return false;
else
return true;
}
Transform is component attached to each gameObject and it holds values for position, rotation and scale, so your "playerCurrentPosition" is not copy of position but rather reference to Transform (not a copy).
Create a new GameObject that is a copy of yours original, and use its transform to make your calculations. (This answer is originally from the Unity forums). The official documentation tells me you can use Object.Instantiate to create a clone of a GameObject.
In C#, objects have always their reference passed as value, so simply reassign won't do it. See this related question.
Objects in C# are passed by reference. If you want to copy an object, implement the ICloneable interface which has the method Clone(). You will need to copy the object yourself and return it from this method.

Confused about object references in C#

Ok,
I have a matrix of enemies Enemy enemyGrid[x, y]
Then, in my code I get an instance of one of the enemies by calling Enemy tmp = enemyGrid[a, b]
But if I change a property in tmp it is not reflected the next time I load the object from the matrix into the the same object tmp.
Each time I am finished with tmp I need to make it = null to have the change reflected into the object in the gird?
Why is that? I thought that tmp would just hold a reference to the object and changes would be made directly in the main object.
Thanks.
CODE UPDATE:
Populating the grid:
Enemy [,] spriteGrid = new Enemy[countCols, countRows];
spriteGrid[x, y] = new Enemy();
Access an object and change properties:
Enemy tmp = spriteGrid[i, j];
tmp.canShoot = true;
tmp.Update(gameTime, game.Window.ClientBounds);
tmp.canShoot = false;
The last line (canShoot = false) does not reflect into the object stored in the grid.
The line
Enemy tmp = enemyGrid[a, b]
does not create a copy of the object in your matrix. It creates an alias to the same object instance. Changes to tmp do affect the instance in the grid that they alias.
Please post a short, complete code snippet that demonstrates the issue you are experiencing.
UPDATE
In your sample, you set
tmp.canShoot = true;
but then
tmpEnemy.canShoot = false;
Two different variables.
Update 2
#Amry's comment is also accurate... if Enemy is a struct instead of a class, you would see this very behavior. That is because struct is a value type, meaning the assignment does create a copy rather than an alias.
Except for very special cases, you should never use a struct that is mutable (that is, a struct whose value can change after it is initially created).

Categories