Related
Problem Statement
There is a custom vector class:
namespace StackoverflowQuestion1
{
public class MyVector
{
public float x;
public float y;
public float z;
public MyVector(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
}
}
There is an interface for anything that's movable, which means positions may change:
namespace StackoverflowQuestion1
{
public interface IMovable
{
public string Name { get; }
public MyVector Position { get; }
}
}
Furniture is movable, therefore it implements the corresponding interface:
namespace StackoverflowQuestion1
{
public class Furniture : IMovable
{
public string Name { get; private set; }
public MyVector Position { get; private set; }
public Furniture(string name, float x, float y, float z)
{
this.Name = name;
this.Position = new MyVector(x, y, z);
}
}
}
Accessing the private getter of the Name is not possible, as expected. Accessing the private setter of Position is also not working, as expected. However, accessing the fields of Position is possible, as they are public.
using StackoverflowQuestion1;
class Program
{
static void Main(string[] args)
{
Furniture F = new Furniture("Chair", 1f, 2f, 3f);
F.Name = "Office chair"; // doesn't work, as expected
F.Position = new MyVector(5f, 6f, 7f); // doesn't work, as expected
F.Position.x = 5f; // works, unfortunately
F.Position.y = 6f; // works, unfortunately
F.Position.z = 7f; // works, unfortunately
}
}
Question
How to make it impossible to change the furniture's position, without making the coordinates of MyVector private and, thus, inaccesible? I want to have encapsulation, by only letting Furniture members access the position, but MyVector will become useless in other places if its values can't be changed.
A couple of points to make here:
By design you chose to make the fields public which means they are readily accessible from other classes. They are not private which is what the title implies. To force them to be read only use the readonly keyword
public class MyVector
{
public readonly float x;
public readonly float y;
public readonly float z;
public MyVector(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
}
Typically you won't expose the fields but instead use properties with getters defined only.
public class MyVector
{
private readonly float x;
private readonly float y;
private readonly float z;
public MyVector(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
public float X { get => x; }
public float Y { get => y; }
public float Z { get => z; }
}
Furthermore, you can simplify things using auto-properties
public class MyVector
{
public MyVector(float x, float y, float z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
public float X { get; }
public float Y { get; }
public float Z { get; }
}
Finially, it recommended for value semantics where (x,y,z) will always go together to use struct declarations.
public readonly struct MyVector
{
public MyVector(float x, float y, float z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
public float X { get; }
public float Y { get; }
public float Z { get; }
}
As a side note, if you try to modify the contents of a struct exposed by a property, the C# is going to complain.
Consider this code
public struct MyVector
{
public float x;
public float y;
public float z;
public MyVector(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
}
public class Movable
{
public Movable(MyVector position)
{
Position = position;
}
public MyVector Position { get; }
}
So even with by design allowing the contents of MyVector to be mutable (change), the compiler is going to stop you. This is because with struct types you have local copies of the data everywhere and by writing Position.x = 10f you would have modified a local copy of Position that exists in the scope where this is called, and not modified the original data.
In the question MyVector is a class and so Position.x = 10f modifies the original data and as stated this is undesirable behavior, so follow the steps above to disallow this behavior.
To make MyVector work well with other classes I often add the following functionality to such deflations. I add support for .ToString() with formatting and I add support for .Equals() (and == for structures) in order to be to write code like this:
static void Main(string[] args)
{
var pos = new MyVector(1f, 1/2f, 1/3f);
var m = new Movable(pos);
if (m.Position == pos)
{
Console.WriteLine($"{m.Position:f2}");
// (1.00,0.50,0.33)
}
}
Notice the formatting with 2 decimals and the equality check.
here is the complete code that allows this for your reference
MyVector.cs
public readonly struct MyVector : IEquatable<MyVector>, IFormattable
{
public MyVector(float x, float y, float z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
public float X { get; }
public float Y { get; }
public float Z { get; }
#region IEquatable Members
/// <summary>
/// Equality overrides from <see cref="System.Object"/>
/// </summary>
/// <param name="obj">The object to compare this with</param>
/// <returns>False if object is a different type, otherwise it calls <code>Equals(MyVector)</code></returns>
public override bool Equals(object obj)
{
if (obj is MyVector other)
{
return Equals(other);
}
return false;
}
public static bool operator ==(MyVector target, MyVector other) { return target.Equals(other); }
public static bool operator !=(MyVector target, MyVector other) { return !(target == other); }
/// <summary>
/// Checks for equality among <see cref="MyVector"/> classes
/// </summary>
/// <param name="other">The other <see cref="MyVector"/> to compare it to</param>
/// <returns>True if equal</returns>
public bool Equals(MyVector other)
{
return X.Equals(other.X)
&& Y.Equals(other.Y)
&& Z.Equals(other.Z);
}
/// <summary>
/// Calculates the hash code for the <see cref="MyVector"/>
/// </summary>
/// <returns>The int hash value</returns>
public override int GetHashCode()
{
unchecked
{
int hc = -1817952719;
hc = (-1521134295) * hc + X.GetHashCode();
hc = (-1521134295) * hc + Y.GetHashCode();
hc = (-1521134295) * hc + Z.GetHashCode();
return hc;
}
}
#endregion
#region Formatting
public override string ToString() => ToString("g");
public string ToString(string formatting) => ToString(formatting, null);
public string ToString(string format, IFormatProvider provider)
{
return $"({X.ToString(format, provider)},{Y.ToString(format, provider)},{Z.ToString(format, provider)})";
}
#endregion
}
The problem with using a private setter for an object is that it only prevents you from replacing the object entirely. As it's not an immutable object, you can still access its properties change them, as you have found.
You could define an IMyVector interface with get only properties, have MyVector implement it, and then use the interface for your public Position property.
public interface IMyVector
{
float x {get;}
...
}
public class MyVector : IMyVector
{
...
}
public class Furniture : IMovable
{
public string Name { get; private set; }
public IMyVector Position { get; private set; }
...
Another design possibility is to declare IMyReadOnlyVector interface and expose it whenever we don't want to allow change vectors:
public interface IMyReadOnlyVector {
float x { get; }
float y { get; }
float z { get; }
}
public interface IMyVector : IMyReadOnlyVector {
float x { get; set; }
float y { get; set; }
float z { get; set; }
}
Then you implement MyVector:
public class MyVector : IMyVector {
public MyVector(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
public float x { get; set; }
public float y { get; set; }
public float z { get; set; }
}
Now, time for the trick: IMovable uses IMyReadOnlyVector interface: we let user see Position but not allow to change it.
public interface IMovable {
string Name { get; }
// User can see position, but not allowed to change it
IMyReadOnlyVector Position { get; }
}
public class Furniture : IMovable {
// Private usage only: we don't want user explicitly change position
private MyVector m_Position;
public string Name { get; private set; }
// Public usage: user can't change vector's coordinates here
public IMyReadOnlyVector Position => m_Position;
public Furniture(string name, float x, float y, float z) {
this.Name = name;
this.m_Position = new MyVector(x, y, z);
}
// But we can change Position within the class
public void ShiftMe(int dx, int dy, int dz) {
m_Position.x += dx;
m_Position.y += dy;
m_Position.z += dz;
}
}
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.
I was doing some reading here about creating immutable object in java and I was wondering, is it okay to create a mutable object in certain situations?
For example, let's say we were creating a ping-pong game in C#, obviously, we would have a class that represents a ball, and the two paddles, would you write the ball class like this:
class Ball
{
private readonly int xPosition;
private readonly int yPosition;
private readonly int ballSize;
private readonly string ballColor;
public Ball(int x, int y, int size, string color)
{
this.xPosition=x;
this.yPosition=y;
this.ballSize = size;
this.ballColor = color;
}
public int getX
{
get
{
return this.xPosition;
}
}
//left out rest of implementation.
or like this:
class Ball
{
private int xPosition;
private int yPosition;
private int ballSize;
private string ballColor;
public Ball(int x, int y, int size, string color)
{
this.xPosition=x;
this.yPosition=y;
this.ballSize = size;
this.ballColor = color;
}
public int getX
{
get
{
return this.xPosition;
}
set
{
this.xPosition = value;
}
}
}
}
In a situation where our object(ball) can change position, size(smaller or larger depending on level) and color, wouldn't it be better to provide a setter property? In this case making it mutable makes sense? How would you approach this?
If you are using c#, you do not need to go thru the overhead of creating separate fields to make objects immutable. Instead you can do something like this -
class Ball
{
public Ball ( int x, int y, int size, string color)
{ ... }
public int XPos {get; private set; }
public int YPos {get; private set; }
public int Size {get; private set; }
public string BallColor {get; private set; }
}
This way, you can still write methods in the class to mutate the properties but nothing outside of the class can change their values.
So I'm quite new to programming in general. I'm currently working on a terrain generation program, everything is going great except for this:
public static class Desert
{
public const int iChance = 15;
public static int chance = iChance;
public static int chancepoint = 0;
public const int octaves = 4;
public const int lengthMin = 60;
public const int lengthMax = 90;
public const float scaleMin = 250;
public const float scaleMax = 350;
public const float persistenceMin = 0.5f;
public const float persistenceMax = 0.9f;
public const pType ptype = pType.Lowland;
public const bTag[] tags = { bTag.desert };
}
public static class Meadow
{
public const int iChance = 45;
public static int chance = iChance;
public static int chancepoint = 0;
public const int octaves = 4;
public const int lengthMin = 45;
public const int lengthMax = 70;
public const float scaleMin = 200;
public const float scaleMax = 470;
public const float persistenceMin = 0.35f;
public const float persistenceMax = 0.70f;
public const pType ptype = pType.noAbs;
public const bTag[] tags = { bTag.lush };
}
These are the properties for each different type of 'Biome'.
I currently have about 7 of these and they're all exactly the same except for the values of each field.
Is there a way that I can shorten the code? I looked into inheritance but I ended up with errors and I got a little confused. ><
It would be brilliant if all I had to write was:
public static class Desert
{
iChance = 15;
chance = iChance;
chancepoint = 0;
octaves = 4;
lengthMin = 60;
lengthMax = 90;
scaleMin = 250;
scaleMax = 350;
persistenceMin = 0.5f;
persistenceMax = 0.9f;
ptype = pType.Lowland;
strongTags = { bTag.desert };
}
Thanks in advance.
Oh, and sorry about the nubness of the question, you would probably scream at how terrible my code was if you saw the rest of the program. XD
EDIT: It's probably wise to tell you that I NEVER change the stuff within the class again with the exception of the value of 'chance'.
Instead of using a static class, you can use a non-static class.
public class Biome {
// Instance fields with default values
public int iChance = 15;
public int chance = iChance;
public int chancepoint = 0;
public int octaves = 4;
public int lengthMin = 60;
public int lengthMax = 90;
public float scaleMin = 250;
public float scaleMax = 350;
public float persistenceMin = 0.5f;
public float persistenceMax = 0.9f;
public pType ptype = pType.Lowland;
public bTag[] tags = { bTag.desert };
}
Here use the constructor for initializing:
public Biome(int iChance, int chance, int chancepoint, int octaves, public int lengthMin, int lengthMax, float scaleMin, float scaleMax, float persistenceMin, float persistenceMax,pType ptype, bTag[] tags) {
// init fields here
}
Then call the constructor:
Biome bimoe = new Biome(15, iChance, 0, 4, 60, 90, 250, 350, 0.5f, 0.9f, pType.Lowland, { bTag.desert });
With this it's difficult to see which parameter goes to which field, but it's much shorter.
If the fields must be read-only, you can make properties with only a public get and no set accessor. Example:
public Chance { get { return chance; } }
In this case make the fields private:
private int chance = iChance;
(Personally, for such a scenario, i would put all the data in a file)
The following would be shorter:
public const int iChance = 15, octaves = 4, lengthMin = 60, lengthMax = 90;
public const float scaleMin = 250, scaleMax = 350, persistenceMin = 0.5f,
persistenceMax = 0.9f;
public static int chance = iChance, chancepoint = 0;
However... these really don't look like things that should be static fields, or quite possibly not even const. They look like things that should be instance properties. Maybe something like:
public class Terrain {
public int Chance {get;private set;}
public int LengthMin {get;private set;}
// ...
private Terrain(int chance, int lengthMin, ...) {
Chance = chance;
LengthMin = lengthMin;
// ...
}
private static readonly Terrain
desert = new Terrain(45, 45, ...),
meadow = new Terrain(15, 60, ...),
...;
public static Terrain Desert { get { return desert;}}
public static Terrain Meadow { get { return meadow;}}
}
I don't know much about terrain generation programs, but you should store your data in a database.
Then create classes to map that data to your application.
I recommend you to lookup "Data structures" and see which one fits your application the best.
It's better to use only one class without inheritance, or even structure. Desert, Meadow and so on are not classes logically, it's have to be objects (maybe constants).
What you could do is use a single class called Terrain and Initialise this multiple times using a static constructor:
public class Terrain
{
public int IChance { get; private set; }
public int Chancepoint { get; private set; }
public int Octaves { get; private set; }
public int LengthMin { get; private set; }
public int LengthMax { get; private set; }
public float ScaleMin { get; private set; }
public float ScaleMax { get; private set; }
public float PersistenceMin { get; private set; }
public float PersistenceMax { get; private set; }
public pType Ptype { get; private set; }
public bTag[] Tags { get; private set; }
public static Terrain Desert()
{
return new Terrain
{
IChance = 15,
Chancepoint = 0,
Octaves = 4,
LengthMin = 60,
LengthMax = 90,
ScaleMin = 250,
ScaleMax = 350,
PersistenceMin = 0.5f,
PersistenceMax = 0.9f,
Ptype = pType.Lowland,
Tags = new bTag[] {bTag.Desert}
};
}
}
joe's answer is good, but the constructor call has far too many unnamed parameters - what does the 350 mean?
This is an ideal candidate for data driven design.
Rather than define all the Biome types in the code, put all the data for the Biome types into a file and read the file at run time. The C# language has a lot of stuff to help you do this, the key word to search for is Serialisation (and here's a link to MSDN about it).
The big advantage is that you can change the data values without needing to recompile the code.
The disadvantage is that is takes a lot more code to define the first instance, but after that, you can easily create as many instances as you want.
You could do something like declaring an abstract class like this and then inherting from it:
public abstract class Terrain
{
public int iChance;
public int chance;
public int chancepoint;
public int octaves;
public int lengthMin;
public int lengthMax;
public float scaleMin;
public float scaleMax;
public float persistenceMin;
public float persistenceMax;
public pType ptype;
public Tag[] strongTags;
}
public class Desert : Terrain
{
}
public enum pType
{
Desert = 1,
LowLand = 2
}
public enum Tag
{
desert = 1,
lush = 2
}
You can then instantiate desert like :
var desert = new Desert()
{
iChance = 15
,chance = 15
,chancepoint = 0
,octaves = 4
,lengthMin = 60
,lengthMax = 90
,scaleMin = 250
,scaleMax = 350
,persistenceMin = 0.5f
,persistenceMax = 0.9f
,ptype = pType.Desert
,strongTags = new Tag[]{Tag.desert}
};
Not sure what your exact requirements are but wouldn't this be a better approach:
public abstract class BiomeBase
{
public int Chance { get; set; }
public int Chancepoint { get; set; }
public int Octaves { get; set; }
// you get the idea ...
}
Then you have Desert and Meadow inheriting:
public class Desert : BiomeBase
{
// everything is inherited ...
// you can also add your own properties meant for Desert only (if needed)
}
public class Meadow : BiomeBase
{
// everything is inherited ...
}
Now Desert has everything Biome has and you can use it like this:
var desert = new Desert
{
Chance = 5,
Octaves = 1,
/// etc
};
Firstly you cant do inheritance on static classes. So you would have to start using instances.
Secondly you would use inheritance if you wanted to extend the object. So for instance if you wanted to add a new property "bool HasScorpions" onto Desert but not on Meadow.
Since your using the same properties but want to use different values I personally use an Interface. This way you can make the properties readonly ect while still easily setting the values.
public interface Terrain
{
int iChance = {get { return 15; private set; } ..and repeat.
int chance = iChance;
int chancepoint = 0;
int octaves = 4;
int lengthMin = 60;
int lengthMax = 90;
float scaleMin = 250;
float scaleMax = 350;
float persistenceMin = 0.5f;
float persistenceMax = 0.9f;
pType ptype = pType.Lowland;
bTag[] tags = { bTag.desert };
}
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".