So I have started using WorldToScreenPoint but the thing is the object is not the same on different screen sizes :(
heres my codes
public virtual void Move()
{
Vector2 buttonFirst = thisCam.WorldToScreenPoint(gameButtons[0].transform.position);
buttonFirst.x = 316.5f; //242
buttonFirst.y = 111f;
gameButtons[0].transform.position = buttonFirst;
}
heres the output
the object is not the same on different screen sizes
Of course, different size so different position.
If you need the return position to be invariable, use WorldToViewportPoint, it always returns a value between (0,0) to (1,1).
as I Understood you need to use ScreenToWorldPoint!
https://docs.unity3d.com/ScriptReference/Camera.ScreenToWorldPoint.html
check this
Related
TextMesh Pro shaders have two unusual facilities for adjusting the Textures used for both the Face and the Outline: Tiling and Offset.
They're not accessible via the normal ways of using keywords to access shader properties in Unity.
Is it possible to access these properties from Monobehaviours? How?
If you're wanting to see sample code... there's little point... as I've tried all the normal ways of accessing shader properties in Unity and found none of them work, all throwing errors relating to symbols not existing. Or returning nulls.
These properties are somewhat nested, somehow.
If you've ever successfully edited these values with a Script in Unity, you'll likely know that they're a bit different.
Within the Shader files for TextMesh Pro, these values are known as:
float4 _FaceTex_ST;
float4 _OutlineTex_ST;
Note, the .x and .y of these float4 are the scaling/tiling along their respective axis, whilst .z and .w are used for their offsets.
Depending a bit on which shader exactly you use - for now assuming one of the built-in ones like e.g. TextMeshPro/Distance Field (Surface) you can search for the shader e.g. in Assets/TextMeshPro/Shaders, select the Shader and now see which properties are exposed in the Inspector.
In that case it would be the _FaceTex texture.
Now the Tiling and Offset are indeed quite tricky since they store those directly along with the Texture property itself! You can see his when setting the Inspector to Debug mode with the TextMeshPro selected
=> You want to use Material.SetTextureOffset and Material.SetTextureScale (= Tiling) with the property name of the texture itself
yourTextMeshPro.fontMaterial.SetTextureScale("_FaceTex", new Vector2(42, 42));
yourTextMeshPro.fontMaterial.SetTextureOffset("_FaceTex", new Vector2(123, 456));
The Tiling and Offset have no effect for the Outline apparently. See e.g. Distance Field (Surface) Shader.
Outline
...
Tiling: ????
Offset: ????
You could still try though and do the same just with _OutlineTex
Thanks to the incomparable derHugo, the resultant code works perfectly, in both directions:
using TMPro;
using UnityEngine;
public class testMaterailProps : MonoBehaviour {
public Vector2 FaceTiling;
public Vector2 FaceOffset;
public Vector2 OutLineTiling;
public Vector2 OutlineOffset;
public Material myFontMaterial;
TextMeshPro myTexmMeshPro;
static readonly int FaceTex = Shader.PropertyToID( "_FaceTex" );
static readonly int OutlineTex = Shader.PropertyToID( "_OutlineTex" );
void Start ( ) {
myTexmMeshPro = GetComponent<TextMeshPro>( );
myFontMaterial = myTexmMeshPro.fontSharedMaterial;
FaceTiling = myFontMaterial.GetTextureScale( FaceTex );
FaceOffset = myFontMaterial.GetTextureOffset( FaceTex );
OutLineTiling = myFontMaterial.GetTextureScale( OutlineTex );
OutlineOffset = myFontMaterial.GetTextureOffset( OutlineTex );
}
void OnValidate ( ) {
myFontMaterial.SetTextureScale( FaceTex, FaceTiling );
myFontMaterial.SetTextureOffset( FaceTex, FaceOffset );
myFontMaterial.SetTextureScale( OutlineTex, OutLineTiling );
myFontMaterial.SetTextureOffset( OutlineTex, OutlineOffset );
}
}
Making it possible to copy text styles accurately from one text object to another, since a bug in the copy/paste of Unity properties prevents these values being copy-able through the Editor UI...
I'm currently working on an FPS and trying to create a "Gamemode" controller that gives the team with the most team members within a defined 3d area a point for their team. My first attempt looks something like:
public class TDM : Gamemode
{
private Collider pointArea;
private Dictionary<Player, int> teamA;
private Dictionary<Player, int> teamB;
private int methodNumberOfPlayersOnPoint(List<Player> players)
{
int count = 0;
foreach (Player player in players)
{
if (pointArea.bounds.Contains(player.transform.position))
{
count++;
}
}
return count;
}
}
is there a better way to do this?
You can call OverlapSphere which gets all the elements within a sphere. Or a box using OverlapBox or capsule using OverlapCapsule.
void GettAll(Vector3 center, float radius)
{
Collider[] objectsHit = Physics.OverlapSphere(center, radius);
foreach (var obj in objectsHit )
{
// do something for each object
}
}
I don't have much experience in Unity yet so I'll let someone else provide code, but I believe what you're looking for are Trigger Colliders. You basically want to let the engine itself do the heavy lifting for checking for intersection of your player with the zone.
Then in the method that gets called when the player enters the area you can add them to the count that is being used by your scorekeeping component for calculating point accrual. When the player leaves you remove them from the count.
If the position alone (ignoring any shape of objects) is enough precise then this is probably already a good approach.
You could/should cache the pointArea.bounds though before the loop so it is fetched only once.
And you could use Linq to simplify the code like
private int methodNumberOfPlayersOnPoint(List<Player> players)
{
var bounds = pointArea.bounds;
return players.Count(player => bounds.Contains(player.transform.position));
}
Note though that the Bounds in Unity are always world axis aligned! That means if your area is rotated in any way the bounds are not accurate.
In such case you might wan to rather use a PhysicsOverlapBox which allows you to check a specific rotation as well. This requires all your objects having colliders of course
private static int methodNumberOfPlayersOnPoint(BoxCollider area)
{
return Physics.OverlapBox(area.transform.TransformPoint(area.center), area.size / 2f, area.transform.rotation).Length;
}
Note for now this returns ALL objects within this area. you might want to filter for a certain LayerMask and include a filter for the Team (e.g. tag or some ID stored in Player)
i use the slider to change the joint angle, here is my code.
void Update()
{
a = sliderx.GetComponent<slider>().value;
b = slidery.GetComponent<slider>().value;
headjoint.transform.Rotate(a, b, 0);
}
and after I slide the bar, the joint rotates around and won't stop. like
a += value
and not
a = value
, the slider not give a definite number?
why? do I need to use eularangle?
when I use
headjoint.transform.rotation = Quaternion.Euler(a, b, 0);
it's change the rotation angle of my object ,to 0,0,0 when running
anyideas?
Rotate as the name suggests rotates the object about a given amount. If you call this constantly with the same value your object is rotated constantly about the same amount.
You rather want to set a rotation.
Your attempt
headjoint.transform.rotation = Quaternion.Euler(sliderX.value, sliderY.value, 0);
is actually close, yes. But by default Rotate is applied in local space. So if you want that same behavior you'd rather use
// First of all use the correct type and don't use GetComponent repeatedly
public Slider sliderX;
public Slider sliderY;
void Update()
{
headjoint.transform.localRotation = Quaternion.Euler(sliderX.value, sliderY.value, 0);
}
I want to get a GameObject's height. I have tried with:
this.GetComponent<MeshRenderer>().bounds.size.y
But the problem with bounds is that it works with static objects only. When you have a moving object and if the rotation of the object is not perfectly aligned, the bounds (height) is not accurate anymore because it returns the height of the bounds that is a square and if you tilt an object like a plate you get the bounds height which is not accurate to the height of the object.
It is an Axis-Aligned Bounding Box (also known as an "AABB").
Please check the image I attached, there you can see the problem with moving objects and if you rotate them how the height is not accurate anymore.
Did anyone else have this kind of problem?
Any advice on how to get the object's height accurately?
One option is to use the Game Object's Collider.
You can relatively easily find the height and width of a collider so this is one way you can measure the dimensions of a game object. The collider, for example a box collider on the game object, will have to fully encompasses the object. You can do this be resizing the collider until it snugly wraps around your game object. Then when you want to find a certain dimension of the object instead find that dimension on the collider and multiply by the Game Object's transform.scale.
Here are some examples I have tested.
Example 1:
CapsuleCollider m_Collider = GetComponent<CapsuleCollider>();
var height = m_Collider.height * transform.localScale.y;
https://docs.unity3d.com/ScriptReference/CapsuleCollider-height.html
or
Example 2:
BoxCollider m_Collider = GetComponent<BoxCollider>();
var height = m_Collider.size.y * transform.localScale.y;
var width = m_Collider.size.x * transform.localScale.x;
var breadth = m_Collider.size.z * transform.localScale.z;
https://docs.unity3d.com/ScriptReference/BoxCollider-size.html
Hopefully one of these will work for you.
If not, then a second inconvenient way you can find the height is to create 2 proxy objects as children at the top and bottom of your object. Afterwards find the scalar distance between them.
Solution 1: Use Mesh.bounds
You can get the height using Mesh.bounds. Unlike MeshRenderer.bounds (or Renderer.bounds), this is the axis-aligned bounding box of the mesh in its local space (i.e. not affected by the transform).
You still need to account for the scale of the GameObject using transform.lossyScale, as follows:
public class Example : MonoBehaviour
{
private Mesh _mesh;
private void Awake()
{
_mesh = GetComponent<MeshFilter>().mesh;
}
private void Update()
{
float height = _mesh.bounds.size.y * transform.lossyScale.y;
Debug.Log(height);
}
}
(Note when using transform.lossyScale, the value can be slightly inaccurate. This is something to note if you need extreme accuracy. See the documentation on transform.lossyScale:)
Please note that if you have a parent transform with scale and a child
that is arbitrarily rotated, the scale will be skewed. Thus scale can
not be represented correctly in a 3 component vector but only a 3x3
matrix. Such a representation is quite inconvenient to work with
however. lossyScale is a convenience property that attempts to match
the actual world scale as much as it can. If your objects are not
skewed the value will be completely correct and most likely the value
will not be very different if it contains skew too.
Solution 2 (Workaround): Cache the unrotated MeshRenderer bounds
Alternatively, if in your context you are able to instantiate the GameObject without a rotation and you only need to rotate the GameObject after initialization, one workaround solution is to cache the bounds from MeshRenderer in the Awake method, as follows:
public class Example : MonoBehaviour
{
private Bounds _initialUnrotatedBounds;
private void Awake()
{
InitializeUnrotatedBounds();
}
private void InitializeUnrotatedBounds()
{
Assert.IsTrue(transform.rotation == Quaternion.identity);
_initialUnrotatedBounds = GetComponent<MeshRenderer>().bounds;
}
private void Update()
{
// Use the cached bounds. Now, even for moving objects,
// it doesn't matter if the rotation changes
float height = _initialUnrotatedBounds.size.y;
Debug.Log(height);
}
}
In my game, there are many enemies and have this script on it, where I am set their navmesh destination by randomly selecting an element from an array of positions. However, sometimes all enemies go at same position, so I am trying to skip previously generated array indices and find a new index. Any suggestions for this? I don't want to use the goto statement.
Here is code I have done so far:
void move_on_fired_positions()
{
navmesh.Resume ();
again:
new_position = Random.Range (0, firedpoints.Length);
if (prev_num != new_position) {
navmesh.SetDestination (firedpoints [new_position].position);
prev_num = new_position;
} else
{
goto again;
}
}
One thing you can try is keeping a dynamic list of available positions, rather than an array. (Don't worry - they're both serialized and modified the same way in the Unity editor interface.) This way, you can remove positions from the list as they are assigned to enemies, ensuring you never assign duplicates. Here's an idea of how that might look:
public List<Transform> firedpoints;
// Returns available firing position, if any
public Transform GetRandomFiringPosition()
{
if (firedpoints.Count > 0)
{
// Get reference to transform, then remove it from list
int newPositionIndex = Random.Range (0, firedpoints.Length);
Transform returnedPosition = firedpoints[newPositionIndex];
firedpoints.RemoveAt(newPositionIndex);
return returnedPosition;
}
else
{
// Or you can specify some default transform
return null;
}
}
// Makes firing position available for use
public void RegisterFiringPosition(Transform newPosition)
{
firedpoints.Add(newPosition);
}
This code should be in a script on a single object in the scene, which the enemies should have a reference to (or, you can change the code a little and make the class into a singleton, so its methods can be called without a direct reference). Whenever an enemy needs a new position, it can call GetRandomFiringPosition() and store the returned Transform in a variable for future reference.
You haven't determined what conditions make a position available for use again, but when you have, you can call RegisterFiringPosition() to get it back into the list. As a nice side effect, this also makes it possible to assign brand new positions to enemies, for example in response to player-triggered events.
Hope this helps! Let me know if you have any questions.