Is WaitForEndOfFrame the same as OnRenderImage? - unity3d

In Unity, is WaitForEndOfFrame
void Update()
{
StartCoroutine(_c());
}
IEnumerator _c()
{
yield return new WaitForEndOfFrame();
BuildTexturesForExample();
}
identical to OnRenderImage?
void OnRenderImage()
{
BuildTexturesForExample();
}
(It goes without saying the minimal/useless unity doco on the two calls does not help.)
If not what is done "after" OnRenderImage until WaitForEndOfFrame is called?
Does anyone have any experience of using the two comparatively?
Safe to replace??????
Can you always safely replace a WaitForEndOfFrame pattern with OnRenderImage?1
What's the deal?
1(Of course, gizmos/ongui are irrelevant.)

I guess I just found the actual answer in OnPostRender
OnPostRender is called after the camera renders all its objects. If you want to do something after all cameras and GUI is rendered, use WaitForEndOfFrame coroutine.
So (other than I thought and the ExecutionOrder makes it look/sound like) all methods in the Render block (except OnGUI and OnDrawGizmos) are called on a per Camera basis and also note that
OnPostRender: This function is called only if the script is attached to the camera and is enabled.
or
OnRenderImage: This message is sent to all scripts attached to the camera.
Its purpose is Post-Processing (I only understood how they work looking at the examples.), therefore it actually takes 2 arguments!
OnRenderImage(RenderTexture src, RenderTexture dest)
so you can overwrite the output texture (dest) with some render effects after receiving the input (src) as in their example
Material material;
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
// Copy the source Render Texture to the destination,
// applying the material along the way.
Graphics.Blit(source, destination, material);
}
There is also e.g. OnRenderObject which is called on all GameObjects (MonoBehaviours) not only the Camera. Again until I saw the example I didn't really understand what it does (or what makes it different from OnRenderImage). But that example here helped:
void OnRenderObject()
{
// Render different meshes for the object depending on whether
// the main camera or minimap camera is viewing.
if (Camera.current.name == "MiniMapcam")
{
Graphics.DrawMeshNow(miniMapMesh, transform.position, transform.rotation);
}
else
{
Graphics.DrawMeshNow(mainMesh, transform.position, transform.rotation);
}
}
Bonus: I finally understand the real purpose of Camera.current! :D
WaitForEndOfFrame on the other hand is called after all cameras finished rendering and everywhere not only on a Camera GameObject.
Waits until the end of the frame after all cameras and GUI is rendered, just before displaying the frame on screen.
So I'ld say No, you can/should not replace WaitForEndOfFrame by using OnRenderImage!

Related

Graphics.RenderTexture() causes extra image to appear on canavas

I have some code like this:
readonly Rect WORK_SOURCE_RECT = new Rect(0f, 0f, 1f, 1f);
Color[] workPixels;
void Start() {
Texture2D workTexture = new Texture2D(256, 256, GraphicsFormat.R8G8B8A8_UNorm,
TextureCreationFlags.None);
workPixels = workTexture.GetPixels();
}
void OnGUI() {
workTexture.SetPixels(workPixels);
workTexture.Apply();
Graphics.DrawTexture(toRect, workTexture, WORK_SOURCE_RECT,
0, 0, 0, 0, renderColor);
}
void Update() {
// Omitted - Some changes are made by code here to the workPixels array.
}
The call to Graphics.DrawTexture() correctly draws the content of workTexture to the screen, just how I want it. But there is a strange side effect...
Any other GUI that is drawn inside of a scene object containing a Canvas component, will show an extra Y-reversed copy of the work texture. (Nevermind the reversal--not the issue.) I don't know why this extra image is drawn. It seems like there is a shared resource between two GUI things I'd hoped were completely unrelated.
In the image shown below, the reversed-face on the right is the unwanted extra render. Strangely it appears when I move to the right side of my scene, so it's like it is in world space. But it will update when GUI-based elements like subtitles are shown.
On Unity 2019.4.13f1 with MacOS.
The solution I found that resolved my problem was camera stacking. I created a second camera that was culled to just UI layer. The first camera had UI layer removed from culling. And then the calls to Graphics.DrawTexture() no longer appeared on the canvas used for UI.

Unity: How to switch scenes and when returning to the original scene, return player to the place they switched their scene

I have two scenes: Main Street & Building Scene
When the player is Main Street, if the player's trigger box touches the building and the player presses "q", the scene would switch to the Building Scene.
I want it so that when the player exits the Building Scene and returns to the Main Street Scene, the player is back to the position they entered the Building Scene that they entered from. Apologies in advance if this doesn't make sense.
sceneSwitchingScript:
public int buildingToLoad;
public Text InputText;
public movement player;
public Vector3 playerPrevPos;
void OnTriggerEnter2D(Collider2D col){
if(col.CompareTag("Player")){
InputText.text = ("[Q] to enter");
if(Input.GetKeyDown("q")){
if (gameObject.tag == "EntryPoint"){
playerPrevPos = new Vector3(player.transform.position.x, player.transform.position.y, player.transform.position.z);
}
//Debug.Log(gameObject.tag);
Application.LoadLevel(buildingToLoad);
}
}
}
void OnTriggerStay2D(Collider2D col){
if(col.CompareTag("Player")){
if(Input.GetKeyDown("q")){
//spawn = new Vector3(player.transform.position.x, player.transform.position.y, player.transform.position.z)
Application.LoadLevel(buildingToLoad);
if (gameObject.tag == "EntryPoint"){
playerPrevPos = new Vector3(player.transform.position.x, player.transform.position.y, player.transform.position.z);
}
}
}
}
void OnTriggerExit2D(Collider2D col){
if(col.CompareTag("Player")){
InputText.text = ("");
}
}
Setting the player's position when they exit the building
public switchScene ss;
void OnTriggerStay2D(Collider2D col){
if(Input.GetKeyDown("q")){
if(col.gameObject.CompareTag("ExitPoint")){
transform.position = ss.playerPrevPos;
}
}
}
However, these two scrips do not work and I'm not sure if this is related but when I make the player do the switch scene thing in-game, this error pops up:
NullReferenceException: Object reference not set to an instance of an object
movement.OnTriggerStay2D (UnityEngine.Collider2D col)
This error message mentions the error on this line:
transform.position = ss.playerPrevPos;
With Unity, the traditional loading of a scene was fairly destructive. There was the concept of setting a GameObject as safe by setting it as DontDestroyOnLoad, which, in a way, removed the object from the scene altogether, which protected it from scene loads.
Unity finally properly implemented multiscene editing and loading. It's essentially DDOL, but done properly.
Now you can actually have multiple scenes loaded at the same time. Now, what that allows you to do is to have a "Manager" scene that handles all of the objects that are common between scenes, and only load (and unload) the specific objects required for that individual scene.
You'd use it like this:
SceneManager.LoadScene("YourScene", LoadSceneMode.Additive);
That would load "YourScene" in ADDITION to the currently loaded scene. Likewise, removing a scene is:
SceneManager.UnloadScene("YourScene");
Now, if you have a Manager scene, you can include in your manager scene a script that holds data for each individual scene. As a hacky example, you might have:
public Vector3 InsideSceneLastPosition { get; set; }
Which you then assign to before loading your outside scene. When you load your inside scene again, you can read InsideSceneLastPosition again to reposition your character.
Here's the link to the LoadSceneAsync page at Unity.
There's more to it than that, for instance, you have to listen for the SceneManager.sceneLoaded event to know when you've actually loaded the next scene, so that you can reposition your GameObejcts. You can find information about that here.
You can see multiscene editing my simply dragging multiple scenes from the Project window into you Hierarchy window. All the scenes listed will additively work together. But you'll have to be careful though, as there's another "gotcha" in that you cant reference objects from one scene to another. You can call scripts cross scene, but you won't be able to drag a game object from one scene, into the object field of a component on a different scene. Don't worry, you'll get the hang on it =)

rigid body moves by addForce but the object itself doesn't

I'm following this video https://www.youtube.com/watch?v=THnivyG0Mvo and This is my shoot function
void Shoot()
{
muzzleFlash.Play();
shootingSound.Play();
RaycastHit hit;
if( Physics.Raycast(fpsCam.transform.position, fpsCam.transform.forward, out hit, range))
{
//Debug.Log(hit.transform.name);
Enemy1 target = hit.transform.GetComponent<Enemy1>();
if (target != null)
{
target.TakeDamage(damage);
}
if(hit.rigidbody != null)
{
Debug.Log("******************");
hit.rigidbody.AddForce( - hit.normal * impactForce);
}
GameObject impactGo = Instantiate(impactEffect, hit.point, Quaternion.LookRotation(hit.normal));
Destroy(impactGo, 0.3f);
}
}
Rigidbody added to target:
Barrel component:
enter image description here
This function is in my Rifle.cs script which is being added to a rifle object. Everything works fine. But, when I hit an object which has a Rigidbody, it doesn't move but I can see in the scene that the Rigidbody is moving when I hit it many times. The Rigidbody of the target is set to 'Use Gravity' and 'Is Kinematic' is not checked. What am I doing wrong ?
Probably the force you are adding is too small, so it needs a lot of shots to make some effect, as #Horothenic says, try to increase the value of the impactForce Variable. Look if the rigidbody, the mesh renderer, and the colliders are attached to the same object in the scene. The title of your question suggests your rigidbody is moving but your render doesn't change.
Give the force a ForceMode. Since you want the object to look shot, I would recommend using impulse and make sure the force is bigger than the objects mass by a big factor.
if(hit.rigidbody != null)
{
Debug.Log("******************");
hit.rigidbody.AddForce((-hit.normal * impactForce), ForceMode.Impulse);
}
See this page for more info. Force Modes in Unity
Try removing the Animator completely, the animations have an option to write defaults, if the movement was edited on animation it misbehaves because it wants to set the default.
After I have done a lot of google search. This answered my question https://forum.unity.com/threads/mesh-renderer-does-not-move-with-parent-rigid-body.204700/. So What I needed to do it to turn off (unchecked) static in the top right of the inspector for the object.
enter image description here

renderWithShader texture passing

i wish to create a nightvision effect with a shader for my camera. I have written the shader for a normal material, in which i mass a noise mask and a texture (in my camera example, the texture should be the image i get from the camera itself).
I have some questions: first, i see that i can pass a shader to the camera using Camera.renderWithShader. The thing is that i don't know how to link the image from what i see through my camera and my shader. I would also like to put the noise mask to my shader and don't know how to pass it. This is different then having a material to which you could link the textures.
I found some code on the net how to link the shader and the camera.. the thing is that i don't know if it's good due to the fact that i can't see the final nightvision effect because i don't know how to pass textures to the camera. I can see the view altering but don't know if it's right.
void Start () {
nightVisionShader = Shader.Find("Custom/nightvisionShader");
Camera.mainCamera.RenderWithShader(nightVisionShader,"");
}
void OnRenderImage (RenderTexture source, RenderTexture destination)
{
RenderTexture sceneNormals = RenderTexture.GetTemporary (source.width, source.height, 24, RenderTextureFormat.ARGB32);
transform.camera.targetTexture = sceneNormals;
transform.camera.RenderWithShader(nightVisionShader, "");
transform.camera.targetTexture = null;
// display contents in game view
Graphics.Blit (sceneNormals, destination);
RenderTexture.ReleaseTemporary (sceneNormals);
}
found how to do it!
void OnRenderImage (RenderTexture source, RenderTexture destination) {
overlayMaterial.SetTexture ("_MainTex", Resources.Load("nightvision/") as Texture2D);
overlayMaterial.SetTexture ("_noiseTex", Resources.Load("nightvision/noise_tex6") as Texture2D);
overlayMaterial.SetTexture ("_maskTex", Resources.Load("nightvision/binoculars_mask") as Texture2D);
overlayMaterial.SetFloat ("_elapsedTime", Time.time);
Graphics.Blit (source, destination, overlayMaterial, 0);
}

Unity3D OffNavMesh jump issue

I have set up Unity navigation meshes (four planes), navigation agent (sphere) and set up automatic and manual off mesh links. It should now jump between meshes. It does jump between meshes, but it does that in straight lines.
In other words, when agent comes to an edge, instead of actually jumping up (like off mesh link is drawn) it just moves straight in line but a bit faster. I tried moving one plane higher than others, but sphere still was jumping in straight line.
Is it supposed to be like this? Is it possible to set up navigation to jump by some curve? Or should I try to implement that myself?
I came by this question, and had to dig through the Unity sample. I just hope to make it easier for people by extracting the important bits.
To apply your own animation/transition across a navmesh link, you need to tell Unity that you will handle all offmesh link traversal, then add code that regularly checks to see if the agent is on an offmesh link. Finally, when the transition is complete, you need to tell Unity you've moved the agent, and resume normal navmesh behaviour.
The way you handle link logic is up to you. You can just go in a straight line, have a spinning wormhole, whatever. For jump, unity traverses the link using animation progress as the lerp argument, this works pretty nicely. (if you're doing looping or more complex animations, this doesn't work so well)
The important unity bits are:
_navAgent.autoTraverseOffMeshLink = false; //in Start()
_navAgent.currentOffMeshLinkData; //the link data - this contains start and end points, etc
_navAgent.CompleteOffMeshLink(); //Tell unity we have traversed the link (do this when you've moved the transform to the end point)
_navAgent.Resume(); //Resume normal navmesh behaviour
Now a simple jump sample...
using UnityEngine;
[RequireComponent(typeof(NavMeshAgent))]
public class NavMeshAnimator : MonoBehaviour
{
private NavMeshAgent _navAgent;
private bool _traversingLink;
private OffMeshLinkData _currLink;
void Start()
{
// Cache the nav agent and tell unity we will handle link traversal
_navAgent = GetComponent<NavMeshAgent>();
_navAgent.autoTraverseOffMeshLink = false;
}
void Update()
{
//don't do anything if the navagent is disabled
if (!_navAgent.enabled) return;
if (_navAgent.isOnOffMeshLink)
{
if (!_traversingLink)
{
//This is done only once. The animation's progress will determine link traversal.
animation.CrossFade("Jump", 0.1f, PlayMode.StopAll);
//cache current link
_currLink = _navAgent.currentOffMeshLinkData;
//start traversing
_traversingLink = true;
}
//lerp from link start to link end in time to animation
var tlerp = animation["Jump"].normalizedTime;
//straight line from startlink to endlink
var newPos = Vector3.Lerp(_currLink.startPos, _currLink.endPos, tlerp);
//add the 'hop'
newPos.y += 2f * Mathf.Sin(Mathf.PI * tlerp);
//Update transform position
transform.position = newPos;
// when the animation is stopped, we've reached the other side. Don't use looping animations with this control setup
if (!animation.isPlaying)
{
//make sure the player is right on the end link
transform.position = _currLink.endPos;
//internal logic reset
_traversingLink = false;
//Tell unity we have traversed the link
_navAgent.CompleteOffMeshLink();
//Resume normal navmesh behaviour
_navAgent.Resume();
}
}
else
{
//...update walk/idle animations appropriately ...etc
Its recommended to solve your problems via animation. Just create a Jump animation for your object, and play it at the correct time.
The position is relative, so if you increase the Y-position in your animation it will look like the object is jumping.
This is also how the Unity sample is working, with the soldiers running around.
Not sure what version of unity you are using but you could also try this, I know it works just fine in 4:
string linkType = GetComponent<NavMeshAgent>().currentOffMeshLinkData.linkType.ToString();
if(linkType == "LinkTypeJumpAcross"){
Debug.Log ("Yeah im in the jump already ;)");
}
also just some extra bumf for you, its best to use a proxy and follow the a navAgent game object:
Something like:
AIMan = this.transform.position;
AI_Proxy.transform.position = AIMan;
And also be sure to use:
AI_Proxy.animation["ProxyJump"].blendMode = AnimationBlendMode.Additive;
If you are using the in built unity animation!
K, that's my good deed for this week.
Fix position in update()
if (agent.isOnOffMeshLink)
{
transform.position = new Vector3(transform.position.x, 0f, transform.position.z);
}