How to remove 3dModel from Viewport [HelixToolKit WPF] - c#

I am trying to create a question game with 3d models involved. I want my app to change the 3d model if the question is right. To do that I added a function called setupQ
Here is the code for it:
// To Setup Next 3dModel
public void SetupQ(string ModelS)
{
// SET 3d Model
ModelVisual3D device3D = new ModelVisual3D();
device3D.Content = Display3d(ModelS);
// Add to view port
viewPort3d.Children.Add(device3D);
}
Display3D Function:
private Model3D Display3d(string model)
{
Model3D device = null;
try
{
//Adding a gesture here
viewPort3d.RotateGesture = new MouseGesture(MouseAction.LeftClick);
//Import 3D model file
ModelImporter import = new ModelImporter();
//Load the 3D model file
device = import.Load(model);
}
catch (Exception e)
{
// Handle exception in case can not file 3D model
MessageBox.Show("Exception Error : " + e.StackTrace);
}
return device;
}
ModelS is the path to the 3d model. [The path does work].
How can I remove the 3d model that I just added and then add a new 3d model instead?
Is there a replace or clear function for the HelixToolKit?

Well I just figured the answer myself, it was quite simple.
To Clear the viewport you can:
viewPort3d.Children.Clear();
This will remove all lights, so you might as well want to add those back.
viewPort3d.Children.Add(new DefaultLights());

You can use:
viewPort3d.Children.Remove(yourModel);

Related

Merge Mechanic finding nearby objects to "Merge in Bulk" [UNITY 3D]

I'm trying to make a top-down game similar to Clash and TopWar with the merge mechanics. At the moment I have the ability to merge using the code below.
private void OnTriggerEnter(Collider collision)
{
Debug.Log("Colliding With: " + collision.transform.name);
colliding = true;
if (collision.transform.gameObject.GetComponent<SoldierController>() == null)
{
return;
}
if (collision.transform.gameObject.GetComponent<SoldierController>().soldierLevel == soldierLevel)
{
canMerge = true;
}
}
private void OnMouseUp()
{
if(colliding)
{
if (canMerge) isMerge(collisionObject);
else
{
//Outline red.
GetComponent<Outline>().enabled = true;
GetComponent<Outline>().OutlineColor = Color.red;
}
}
}
Then OnMouseUp() resets the local variables for another object to be dragged. The question I have now is that in games like Evermerge and TopWar, when you merge one object with another if there are items of the same tier you can merge as well then you can merge them all at once. I added a screenshot for reference.
I'm building it in 3D and I think my approach with the colliders might be wrong. I've thought about maybe trying to find similar objects from the perspective of the grid to see if there are any objects on the grid that are 5 up or 5 across, but I'm not sure how to use the Grid object like this other than using it as a reference to snap objects into place when being dragged. Let me know if you need any other details. Thanks in advance!

How can I get Time.time as a fixed integer after an event happens in Unity

I want to display on the screen that the player has achieved their goals in this much time after playing the game. But when I display Time.time it keeps changing. I want to record the time elapsed when the event happens and display it as a fixed number string.
When the player achieves the goal, store Time.time in a class variable (i.e. declared inside the class but outside of any function). Then display that variable rather than Time.time.
Do you want to record the result of the game? If so, you can try to create a new canvas in the scene, and create a new image in the canvas as the background image. You can set the relevant properties of the image through the Inspector interface.
After setting the background, add a Button to the scene, and set the different states of the Button when it is pressed, lifted, and disabled according to personal needs. And add a C# script file under canvas - StartMenuController
The start interface code is:
public void OnStartGameButtonClick()
{
//When the button is pressed, jump to the game interface
SceneManager.LoadScene(1);
//The parameter in the LoadScene() function can be the number of interfaces in builtsettings or the scene name
}
The end interface code is:
public void OnExitButtonClick()
{
Application.Quit();
}
Point each function in the canvas to the onClick of the respective button.Hope it helps you thank you
Access the data generated by the player, and then read it out. Does this idea help you?
Create a new C# code in Unity, copy the following code into it and mount it on an empty object. The following code is divided into two methods: SaveGame (save data) and LodeGame (read data). You can create two Buttons in the game and create click events respectively.
public Inventory myInventory;//Data to be saved
public Item HpMedicine;//Data to be saved
public void SaveGame() //Save game method
{
Debug.Log(Application.persistentDataPath);
if (!Directory.Exists(Application.persistentDataPath + "/game_SaveGame"))
{
Directory.CreateDirectory(Application.persistentDataPath + "/game_SaveGame");
}
BinaryFormatter formatter = new BinaryFormatter();//Binary conversion
FileStream file = File.Create(Application.persistentDataPath + "/game_SaveGame/Inventory.txt");
var json0 = JsonUtility.ToJson(myInventory);//Data Json conversion to be saved
var json1 = JsonUtility.ToJson(HpMedicine);//Data Json conversion to be saved
var json2 = JsonUtility.ToJson(MpMedicine);//Data Json conversion to be saved
formatter.Serialize(file, json0);//Saved data
formatter.Serialize(file, json1);//Saved data
formatter.Serialize(file, json2);//Saved data
file.Close();
}
public void LoadGame()//Read the game method
{
BinaryFormatter bf = new BinaryFormatter();
if (File.Exists(Application.persistentDataPath + "/game_SaveGame/Inventory.txt"))
{
FileStream file = File.Open(Application.persistentDataPath + "/game_SaveGame/Inventory.txt", FileMode.Open);
JsonUtility.FromJsonOverwrite((string)bf.Deserialize(file), myInventory);//Saved data
JsonUtility.FromJsonOverwrite((string)bf.Deserialize(file), HpMedicine);//Saved data
JsonUtility.FromJsonOverwrite((string)bf.Deserialize(file), MpMedicine);//Saved data
file.Close();
}
}
I hope I misunderstood your question because as I understand it, this is the answer.
just save Time.time into some varaible on event.
// for example..
public float attackTime;
public void OnAttack() => attackTime = Time.time; // now use attackTime...

How can I Instantiate a model and dissociate the material/texture from another instance of the model?

So the title might not be perfect, but it's essentially what I want to do.
My problem
I'm creating an app using Unity 2017.3.0f3 and Google's ARCore where you can select a model to place in the world and change the texture on the material as desired. The problem I'm having is that I can place a model with 1 texture on it, but when I change the texture on the Ghost model (in this case, the Ghost model floats around in the world on the ARCore Plane where a Raycast from the center of the screen collides with the Plane) it changes the texture on the already-placed model as well. See the screenshots below, taken seconds apart from screenshots on my phone:
I have placed a model (left/back) with the 'Lit' firepit texture, and the Ghost model is still 'Lit' (right/front):
I have clicked the 'Unlit' firepit texture in the list, and now both models have changed texture:
This is the code I wrote to change the Ghost to the selected model (it works, I've tested it with multiple models, but the demo currently only has 1), which is called when clicking on the model thumbnail in the bottom pane:
public void SetPlaceableModel(GameObject newModel)
{
PlaceableModel = newModel;
var pos = (Ghost != null) ? Ghost.transform.position : FirstPersonCamera.ScreenToWorldPoint(new Vector3(Screen.width / 2f, Screen.height / 2f, -100f));
var rot = (Ghost != null) ? Ghost.transform.rotation : Quaternion.identity;
Destroy(Ghost);
Ghost = Instantiate(PlaceableModel, pos, rot);
}
This is the code I wrote to change the texture on the Ghost model, which is called when clicking on the texture thumbnail in the top pane:
public void ChangeGhostTextures(TextureSet textureSet)
{
SelectedTextureSet = textureSet;
var materials = Ghost.GetComponentsInChildren<Renderer>().SelectMany(x => x.materials);
foreach (var texture in textureSet.Textures.Where(x => !x.name.ToLower().Contains("normal")))
{
var normalTexture = textureSet.Textures.FirstOrDefault(x => x.name == string.Format("{0}_Normal", texture.name));
foreach (var material in materials.Where(x => x.name.Replace(" (Instance)", string.Empty) == texture.name))
{
material.SetTexture("_MainTex", texture);
if (normalTexture != null)
{
material.SetTexture("_BumpMap", normalTexture);
}
}
}
}
This is the definition of TextureSet:
public class TextureSet : MonoBehaviour {
public string SetName;
public Texture Thumbnail;
public List<Texture> Textures = new List<Texture>();
}
And this is the code I'm using to Instantiate the Ghost model into a new object and placing it on the ARCore Plane in the Update() method:
var productModel = Instantiate(Ghost, Ghost.transform.position, Ghost.transform.rotation);
productModel.GetComponent<PlaneAttachment>().Attach(hit.Plane);
As far as I can tell, and I don't know why this is, when I Instantiate the Ghost onto the Plane, the materials seem to be maintaining reference to the Ghost materials. I've tried changing the ChangeGhsotTexture to do this:
var _tex = Instantiate(texture) as Texture; //also tried 'as Texture2D'
material.SetTexture("_MainTex", _tex);
But then this happens when I change textures:
I've also tried to Instantiate(material) as Material and then set it that way, but the same thing happens (as the original issue, not the black texture):
material.SetTexture("_MainTex", texture);
if (normalTexture != null)
{
material.SetTexture("_BumpMap", normalTexture);
}
var _mat = Instantiate(material) as Material;
//can't assign directly to `material` because it's the enumeration result
var assignableMaterial = materials.First(x => x.Equals(material));
assignableMaterial = _mat;
Now here's the kicker
After placing a model down, if I click on the model thumbnail (bottom pane, which calls SetPlaceableModel), even when it's the same model that's already selected because it still calls SetPlaceableModel, it works**! I've tried calling SetPlaceableModel after placing the model in the Update() method, but it doesn't fix the issue (to add information, when clicking on the model thumbnail it calls SetPlaceableModel in the Update() from a different script). Also, it now seems to be working properly only for the first model that is is placed, then it behaves with the problem I'm having (but I don't know why... I haven't changed anything).
I've tried...
As I mentioned, I've tried calling SetPlaceableModel again after placing a model, I've tried doing Instantiate on the Texture and Material as is suggested here (which seems to be my exact issue... so I must be missing something). **
I am super confused... please help. If you need any more information let me know.
**
EDIT: on further testing (I added a new model and 2 TextureSet objects to go with it, so I had 2 models each with 2 different texture sets), this 'works' because the issue only arises after the first time I change textures after selecting a model. The issue goes away when I change models and use the material/texture that are default on the model (each model has material(s)/texture(s) on them, set in the Editor, before running the project).
EDIT 2: as #Programmer suggested, I changed the Instantiate(material) as Material to his suggested new Material(material), but it did not work. This is how I implemented it:
public void ChangeGhostTextures(TextureSet textureSet)
{
var materials = Ghost.GetComponentsInChildren<Renderer>().SelectMany(x => x.materials);
foreach (var texture in textureSet.Textures.Where(x => !x.name.ToLower().Contains("normal")))
{
var normalTexture = textureSet.Textures.FirstOrDefault(x => x.name == string.Format("{0}_Normal", texture.name));
foreach (var material in materials.Where(x => x.name.Replace(" (Instance)", string.Empty) == texture.name))
{
material.SetTexture("_MainTex", texture);
if (normalTexture != null)
{
material.SetTexture("_BumpMap", normalTexture);
}
var _material = new Material(material);
var assignableMaterial = materials.First(x => x.Equals(material));
assignableMaterial = _material;
}
}
}
did I do something wrong? I tried this implementation, too, but it doesn't change textures at all now (whereas the other implementation does change textures, but maintains the issue):
var _material = new Material(material);
_material.SetTexture("_MainTex", texture);
if (normalTexture != null)
{
_material.SetTexture("_BumpMap", normalTexture);
}
var assignableMaterial = materials.First(x => x.Equals(material));
assignableMaterial = _material;
when I Instantiate the Ghost onto the Plane, the materials seem to be
maintaining reference to the Ghost materials.
There are three constructor overloads for the Material class:
public Material(string contents);
public Material(Shader shader);
public Material(Material source);
The last is what you need. Use it to make a copy of your original material then assign that copy to your object. This should solve the material reference issues.
public Material objMat;
void Start()
{
Material newMat = new Material(objMat);
//Use the newMat on your object
}
Since I couldn't get this to work with #Programmer's method (though it may work for others, mine was still changing the material, no matter where/how I implemented var _mat = new Material(mat); or assigned textures, I opted to just define multiple materials as Assets and switch between them as desired:
public void ChangeGhostMaterials(MaterialSet materialSet)
{
foreach (var renderer in Ghost.GetComponentsInChildren<Renderer>())
{
var material = materialSet.Materials.FirstOrDefault(x => x.name.Equals(renderer.material.name.Replace(" (Instance)", string.Empty)));
renderer.material = material ?? renderer.material;
}
}
If someone else comes along this and provides a working Answer, I will gladly change the selected Answer, but as it stands this is my solution to my issue.

No line between points in Gmap.Net route

I'm using Gmap.Net on windows form, I want to draw track of an object when I receive its position, I use Routes for this. When I add points to a route, no line is seen on the map, but when I change the zoom of the map, they appear on the map. Also when I set the position of the map after adding a point to the route (gMapControl1.Position = new PointLatLng(...)), it works correctly and I see the route lines on the map, any idea? My code is like below.
void NewDataReceived(DeviceInfo deviceinf)
{
//---some codes
//----For the first time I add layer and route
if (deviceOverLay == null)
{
deviceOverLay = new GMapOverlay(deviceinf.DeviceId.ToString());
gMapControl1.Overlays.Add(deviceOverLay);
deviceRoute = new GMapRoute(new List<PointLatLng>(), deviceinf.DeviceName);
deviceOverLay.Routes.Add(deviceRoute);
//Add all your points here
deviceRoute.Points.Add(new PointLatLng(deviceinf.Latitude, deviceinf.Longitude));
deviceRoute.Tag = deviceinf;
}
else
{
deviceOverLay.Routes[0].Points.Add(new PointLatLng(deviceinf.Latitude, deviceinf.Longitude));
}
//if I call this line it works, but I don't want it
// gMapControl1.Position = new PointLatLng(deviceinf.Latitude, deviceinf.Longitude);
//---some codes
}
Try using
gMapControl1.UpdateRouteLocalPosition(deviceRoute);
This updates the local positions and does a redraw.

Loading a 3D Model outside the XNA Game class

I'm embedding my XNA Game inside a winforms' control. Because of this, I need to subclass Control, not Game.
How can I still load models from my content Project?
This is my code as I have it:
namespace KinectGraphics.XNAEmbedding {
class XNARenderControl : GraphicsDeviceControl {
Game selfGame;
public XNARenderControl() {
selfGame = new Game();
}
protected void LoadContent() {
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
model = selfGame.Content.Load<Model>("Ka-60");
//model = Content.Load<Model>("earth");
//model = Content.Load<Model>("3dm-tie-f-gt");
}
However, when execution reaches the selfGame.Content.Load, it throws ContentLoadException: Error loading "Ka-60". File not found.
What can I do to load the model anyway?
You haven't added the Root directory. You need to specify the full path to the model.

Categories