Im trying to create a seesaw with ball on its shape, that based on the shapes angle, the ball rolls.
Here is the screenshot of it.
So, the shape of the seesaw moves based on the angle generatated by a trackbar value.
Here are the variables declared:
private const float ONE_DEGREE = 0.0174532924f;
private ID3DMesh tab;
private ID3DMesh ball;
The 'tab' variable is the shape.
This method sets the angle of the shape:
public void setShapeAngle(float degree)
{
tabTargetAngle = Util.DegreeToRadian(degree);
}
And here is the method that updates it:
public void Update(int elapsedTime)
{
if (tab.Pitch != tabTargetAngle)
{
if (tabTargetAngle > tab.Pitch)
{
if (tab.Pitch >= (tabTargetAngle - ONE_DEGREE))
{
tab.Pitch = tabTargetAngle;
}
else
{
tab.Pitch += tabuaSpeed * elapsedTime;
}
}
else if (tabTargetAngle < tab.Pitch)
{
if (tab.Pitch <= (tabTargetAngle + ONE_DEGREE))
{
tab.Pitch = tabTargetAngle;
}
else
{
tab.Pitch -= tabuaSpeed * elapsedTime;
}
}
}
}
All of the objects, are ID3DMesh objects. Here is the code of the ID3DMesh class.
public interface ID3DMesh : IDisposable
{
Color Ambient { get; set; }
CollisionTestMethod CollisionDetectionMethod { get; set; }
Mesh D3DXMesh { get; }
Color Diffuse { get; set; }
Color Emissive { get; set; }
Material[] Materials { get; set; }
ID3DMesh Parent { get; set; }
float Pitch { get; set; }
Vector3 PivotOffset { get; set; }
float PivotOffsetX { get; set; }
float PivotOffsetY { get; set; }
float PivotOffsetZ { get; set; }
Vector3 Position { get; set; }
RenderOptions RenderSettings { get; set; }
float Roll { get; set; }
Vector3 Scale { get; set; }
float ScaleX { get; set; }
float ScaleY { get; set; }
float ScaleZ { get; set; }
Color Specular { get; set; }
float SpecularSharpness { get; set; }
Texture[] Textures { get; set; }
Color WireColor { get; set; }
float X { get; set; }
float Y { get; set; }
float Yaw { get; set; }
float Z { get; set; }
MeshBoundingBox GetBoundingBox();
MeshBoundingSphere GetBoundingSphere();
float GetDepth();
float GetHeight();
float GetWidth();
Matrix GetWorldMatrix();
bool Intersects(ID3DMesh mesh);
void Link(ID3DMesh parentMesh, Vector3 linkPosition);
void Move(float xAmount, float yAmount, float zAmount);
void Render();
void RenderPlanarShadow(Plane groundPlane, Light light, bool allowDoubleBlending);
void SetDepth(float depth);
void SetDepth(float depth, bool uniformScale);
void SetHeight(float height);
void SetHeight(float height, bool uniformScale);
void SetPlanarShadowOpacity(float shadowOpacity);
void SetScale(float amount);
void SetScale(float xAmount, float yAmount, float zAmount);
void SetSize(float width, float height, float depth);
void SetWidth(float width);
void SetWidth(float width, bool uniformScale);
}
I tried to use the Move(float, float, float) method. But it didnt moved as it should. If you could help me with that.
Thank you.
(Note: Below I'll be ignoring the third dimension, because the ball will always move along the same plane)
If we take the seesaw as a reference frame, I think the movement of the ball will be similar to that of an harmonic oscillator. That is to say, the position of the ball along the seesaw at a given instant of time, s(t), will be given by the following formula:
s(t) = L cos(2π t / T + ϕ)
where L is the length of the seesaw (the amplitude of the harmonic) and T is the time it takes the ball to move from one end of the seesaw to the other and back to the start (the period of the harmonic). ϕ, the initial phase of the harmonic, is there to adjust the formula so s(0) gives you the starting position. If you want it to start at the center, you need to make s(0) = 0, which means you need the cosine to be 0. So you have to make ϕ be π/2 (90 degrees), because cos(π/2) = 0.
With this you can put the ball in place by changing the world transform. If you rotate it to the current angle of the seesaw (let's call it θ(t)), you can just translate the ball by the value of s(t) along the xx axis.
This is equivalent to treating (s(t),θ(t)) as the position of the ball in polar coordinates. You can then get the cartesian coordinates at a given time (x(t),y(t)) with these formulae:
x(t) = s(t) cos(θ)
y(t) = s(t) sin(θ)
(Let's assume the up-vector is (0, 1, 0) and the tab is aligned with the X-axis)
You can imagine the ball would have to "roll" down the tab along the X-axis, and you would have to calculate the Y-position to let it stick to the tab.
You could use the Move() method for the X position, as the ball's speed has an impact on it's X-position in a relative way.
The Y-position though (as long as the ball remains on the tab) could more easily be calculated for each X-position, by setting the Y property.
If I were you I'd start by creating a method that calculates the Y position to make the ball "stick to the tab" for any X position.
If this doesn't point you in the right direction, please elaborate a bit more on "it didnt moved as it should".
Related
This is a code for MonoGame 3.8.1 and .NET 6.
public interface IEntity
{
int Size { get; }
Vector2 Position { get; }
}
public class Player : IEntity
{
public int Size { get; } = 32;
public Vector2 Position { get; private set; }
public Player(Scene scene)
{
var spawnOffset = new Vector2(0, -3);
Position = new Vector2(
spawnOffset.X * scene.TileSize + scene.Room.Center.x - Size / 2,
spawnOffset.Y * scene.TileSize + scene.Room.Center.y - Size / 2);
}
}
public class Enemy : IEntity
{
public int Size { get; } = 32;
public Vector2 Position { get; private set; }
public Enemy(Scene scene)
{
var spawnOffset = new Vector2(0, 0);
Position = new Vector2(
spawnOffset.X * scene.TileSize + scene.Room.Center.x - Size / 2,
spawnOffset.Y * scene.TileSize + scene.Room.Center.y - Size / 2);
}
}
Without the use of inheritance, I want every class that implements IEntity to be able to reuse calculations for Position assignment. The only difference between both constructors is the spawnOffset values.
What's the most simple way to do this?
Normally - I would call the base class constructor, but here I'm trying to do this without a base class.
Looks like you follow "prefer composition over inheritance" - indeed that is a valid approach, just finish that by extracting "position" functionality into separate class:
class EntityPosition
{
public Vector2 Position { get; private set; }
public EntityPosition (Scene scene, Vector2 spawnOffset, int size)
{
Position = new Vector2(
spawnOffset.X * scene.TileSize + scene.Room.Center.x - size / 2,
spawnOffset.Y * scene.TileSize + scene.Room.Center.y - size / 2);
}
// add other shared methods for position updates here as necessary
}
public class Player : IEntity
{
EntityPosition position;
public int Size { get; } = 32;
public Vector2 Position => position.Position;
public Player(Scene scene)
{
var spawnOffset = new Vector2(0, -3);
position = EntityPosition(scene, spawnOffset, Size);
}
}
I have a problem. I have a few hexagons drawn in SkiaSharp. All the corners are stored in a List<HexagonRegistryList>.
Now I want to know how many shared edges the a hexagon has with the rest of the hexagons, so I created this code:
public class HexagonRegistryList
{
public int HexagonNum { get; set; }
public float x1 { get; set; }
public float y1 { get; set; }
public float x2 { get; set; }
public float y2 { get; set; }
public float x3 { get; set; }
public float y3 { get; set; }
public float x4 { get; set; }
public float y4 { get; set; }
public float x5 { get; set; }
public float y5 { get; set; }
public float x6 { get; set; }
public float y6 { get; set; }
public int ShapeNum { get; set; }
public HexagonRegistryList()
{
this.AdjacentShapeNumbers = new List<int>();
}
public List<int> AdjacentShapeNumbers { get; set; }
public IEnumerable<(float x, float y)> GetPoints()
{
yield return (x1, y1);
yield return (x2, y2);
yield return (x3, y3);
yield return (x4, y4);
yield return (x5, y5);
yield return (x6, y6);
}
public bool IsAdjacentTo(HexagonRegistryList other)
{
var isAdjacentTo = GetPoints().Intersect(other.GetPoints()).Count() >= 2;
if (isAdjacentTo)
{
if (other.ShapeNum != 0)
{
AdjacentShapeNumbers.Add(other.ShapeNum);
}
}
return isAdjacentTo;
}
}
Now this code works, except for points that are not exactly the same but have a difference of 0.001 pixel. So I want to change this to a comparison function which checks if the point of the other corner is closer then 1 pixel away, because that should also be allowed. Can someone help me with this.
I already tried this code, but that just ignores it and still returns false:
public struct PointComparer : IEqualityComparer<(float x, float y)>
{
public bool Equals((float x, float y) p1, (float x, float y) p2)
{
return Math.Abs(p1.x - p2.x) < 1f && Math.Abs(p1.y - p2.y) < 1f;
}
public int GetHashCode((float x, float y) obj)
{
return obj.GetHashCode();
}
}
then I changed this line:
var isAdjacentTo = GetPoints().Intersect(other.GetPoints()).Count() >= 2;
to:
var isAdjacentTo = GetPoints().Intersect(other.GetPoints(), new PointComparer()).Count() >= 2;
But like I already told u, that doesn't work...
Can someone tell me how to make this work?
Since the hexagons all have the same rotation degree and same size you can simply the problem quite a lot.
The main idea is to calculate the distance between two hexagons' center. If the distance between their center point is 2x radius ± 1pixel (or whatever epsilon you wish) then they "might" share one edge.
Now take the vector between the two center point and calculate the angle of the vector. If (angle - 30) % 60 == 0 then they share one edge.
Special case: if two hexagons completely overlap then they will have the same center point
This way you only have to do two comparison with every other hexagons instead of 6x6 comparisons if you do the edge compare way.
Implementation should be quite trivial. The only tricky part would be to apply math formula to find the hexagon's center point. So I will leave it as exercise.
Edit:
Since its a hexagon grid, you got multiple ways of solving this. The center point way still applies. As long as the difference between the center points is less than 2x radius + 1pixel they share one edge. Or you can calculate the distance between two line segments. If the distance between any edge a in hexagon A and any edge b in hexagon B is less than the radius then those two hexagon shares an edge.
I have a problem. I created a TriangleGrid using SkiaSharp. While I was drawing the grid I saved each triangle info in a Dictionary. The Dictionary looks like this:
public class TriangleRegistryObject
{
public float x1 { get; set; }
public float y1 { get; set; }
public float x2 { get; set; }
public float y2 { get; set; }
public float x3 { get; set; }
public float y3 { get; set; }
public bool Selected { get; set; }
public bool Visible { get; set; }
}
Now when I select a Triangle I set the boolean Selected to true. At the end I want to check if the Triangles I have selected are connected with eachother. I thought I could count the connected lines. Here is an example image:
Now I want to count the purple lines where Selected=true.
I have every coordinate (x1, y1) (x2, y2) and (x3, y3).
UPDATE:
Here is the code I use that return 0 for me!
public static bool ValidLayout()
{
bool IsValid;
int sharedEdges;
int SelectedTriangles = TriangleRegistry.Count(tr => tr.Value.Selected.Equals(true));
var triangles = new List<TriangleRegistryList>();
foreach (KeyValuePair<string, TriangleRegistryObject> row in TriangleRegistry.Where(n => n.Value.Selected == true).ToList())
{
triangles.Add(new TriangleRegistryList { x1 = row.Value.x1,
y1 = row.Value.y1,
x2 = row.Value.x2,
y2 = row.Value.y2,
x3 = row.Value.x3,
y3 = row.Value.y3
});
}
sharedEdges = triangles.GetKCombs(2).Where(t => t.First().IsAdjacentTo(t.Skip(1).Take(1).Single())).Count();
if (sharedEdges >= (SelectedTriangles - 1))
{
IsValid = true;
}
else
{
IsValid = false;
}
return IsValid;
}
But I have no idea how I can compare the coordinates with each other, to count the connected lines!
Can someone help me?
Here is a very simple solution. It definitely isn't the most efficient, but it gets the job done.
I've added a method to your triangle class that returns true if it shares at least 2 vertices with another triangle.
I've also used a method of finding the distinct permutations that is slightly modified from the one discussed here.
public class Program
{
public static void Main()
{
var triangles = new List<TriangleRegistryObject>{
new TriangleRegistryObject{x1=10,y1=10, x2=12,y2=10, x3=1,y3=11},
new TriangleRegistryObject{x1=9,y1=11, x2=11,y2=11, x3=10,y3=10},
new TriangleRegistryObject{x1=9,y1=11, x2=11,y2=11, x3=10,y3=12},
new TriangleRegistryObject{x1=34,y1=14, x2=15,y2=11, x3=10,y3=12},
};
var sharedEdges = triangles.GetPairs().Where(t => t.first.IsAdjacentTo(t.second)).Count();
Console.WriteLine($"Number shared edges: {sharedEdges}");
}
}
public class TriangleRegistryObject
{
public float x1 { get; set; }
public float y1 { get; set; }
public float x2 { get; set; }
public float y2 { get; set; }
public float x3 { get; set; }
public float y3 { get; set; }
public bool Selected { get; set; }
public bool Visible { get; set; }
public IEnumerable<(float x, float y)> GetPoints()
{
yield return (x1, y1);
yield return (x2, y2);
yield return (x3, y3);
}
public bool IsAdjacentTo(TriangleRegistryObject other)
{
return this.GetPoints().Intersect(other.GetPoints()).Count() >= 2;
}
}
public static class EnumerableExtensions
{
public static IEnumerable<(T first, T second)> GetPairs<T>(this IEnumerable<T> list)
{
return list.SelectMany((value, index) => list.Skip(index + 1),
(first, second) => (first, second));
}
}
I created this class called Genes and I want to get all it's properties inside of another script. I tried using
PropertyInfo[] props = typeof(Genes).GetProperties();
but props is an empty array. I think typeof isn't working. This is my class Genes:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Genes
{
private float size;
private float speed;
private float probability;
private int color;
public Genes(float size, float speed, float probability, int color)
{
this.size = size;
this.speed = speed;
this.probability = probability;
this.color = color;
}
}
and I basically want to run over size, speed, probability and color using a foreach loop. What is the problem?
Either make your fields properties which have getters and setters:
public class Genes
{
private float Size { get; set; }
private float Speed { get; set; }
private float Probability { get; set; }
private int Color { get; set; }
public Genes(float size, float speed, float probability, int color)
{
this.Size = size;
this.Speed = speed;
this.Probability = probability;
this.Color = color;
}
}
or use GetFields if you really want fields:
typeof(Genes).GetFields();
No matter what you chose, these members should furthermore either be public, or you have to use the BindingFlags.NonPublic to look for non-public members:
typeof(Genes).GetProperties(BindingFlags.NonPublic);
or
typeof(Genes).GetFields(BindingFlags.NonPublic);
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 6 years ago.
I got 2 same classes, one for the primary weapon and the other for the secondary one. Here are my classes :
public class weapon : MonoBehaviour {
public string Name { get; set; }
public float Damages { get; set; }
public float FireRate { get; set; }
public float Range { get; set; }
public float BulletSpeed { get; set; }
public bool isOn { get; set; }
public weapon(string name, float damages, float fireRate, float range, float bulletSpeed, bool ison) {
this.Name = name;
this.Damages = damages;
this.FireRate = fireRate;
this.Range = range;
this.BulletSpeed = bulletSpeed;
this.isOn = ison;
}
}
public class weapon1 : MonoBehaviour {
public string Name { get; set; }
public float Damages { get; set; }
public float FireRate { get; set; }
public float Range { get; set; }
public float BulletSpeed { get; set; }
public bool isOn { get; set; }
public weapon1(string name, float damages, float fireRate, float range, float bulletSpeed, bool ison) {
this.Name = name;
this.Damages = damages;
this.FireRate = fireRate;
this.Range = range;
this.BulletSpeed = bulletSpeed;
this.isOn = ison;
}
}
I want in another script that when I press 'F', the weapons take respectively their specs, but it gets stuck at giving the name to the second one, here is my script to give them their specs:
void Update () {
if (Input.GetKeyDown (KeyCode.F)) {
GetComponent<weapon> ().Name = "Rien";
GetComponent<weapon> ().Damages = 0;
GetComponent<weapon> ().FireRate = 0;
GetComponent<weapon> ().Range = 0;
GetComponent<weapon> ().BulletSpeed = 0;
GetComponent<weapon> ().isOn = true;
Debug.Log (GetComponent<weapon> ().Name);
GetComponent<weapon1> ().Name = "Rien1"; //stuck here... :'(
GetComponent<weapon1> ().Damages = 0;
GetComponent<weapon1> ().FireRate = 0;
GetComponent<weapon1> ().Range = 0;
GetComponent<weapon1> ().BulletSpeed = 0;
GetComponent<weapon1> ().isOn = false;
Debug.Log(GetComponent<weapon1> ().Name);
}
}
I'm getting 'Object reference not set to an instance of an object' and I did exactly the same thing for both weapons
Thanks in advance
You are making some basic programming errors as well as some basic Unity specific errors. As said in the comments, you shouldn't have lots of classes that are exactly the same. Remember to not repeat your self.
Then you are inheriting from MonoBehaviour but are defining a constructor method to set your fields. Don't do this in unity. Make the feilds available in the inspector by making them public or adding the [SerializeField] attribute to private member variables. If you have anything you want to do when the object is created then put this in the Start() method:
public Weapon : MonoBehviour
{
public string name; //set these in the inspector for each weapon
public float damage;
//etc
// in Unity you use the Start method for mono behaviours. NOT the constructor method
void Start()
{
//Do things when the object is created
}
}
Now, it's not clear how you are adding weapons to your player from your question but what you should do is add a game object as a child to the player object in the hierarchy. Do this for each weapon and then add your weapon script to each weapon. Then change the fields that you made available to yourself in the inspector. You can then get a reference to the weapons that the player currently has via a script on the player and do as you please with their values when you press a key.