Smooth SDL2 sprite movement - c#
I make a scene with parallax effect. I can’t make the sprite move smoothly along the X axis. The problem is that regardless of whether I use SDL_RenderCopyF or SDL_RenderCopy, the sprite is drawn according to the pixel grid of the monitor when moving slowly, which is accompanied by a jitter effect,
when there are many layers and everyone moves at different speeds.
My problem is like SDL2 Smooth texture(sprite) animation between points in time function
But there was a version of SDL2 that did not support floating-point rendering, and the author had to patch it. Starting from version 2.0.10.0, SDL_RenderCopyF, SDL_FRect and other functions have been added to the SDL, with which you can implement subpixel moving sprites, but I can not get them to work in my scene.
There are 10 layers in FullHD resolution. Rendering occurs in the FullHD window.
Window and render initialization code:
void CreateWidnow()
{
var windowFlags = Settings.Fullscreen ?
SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN : Settings.Resizeble ?
SDL.SDL_WindowFlags.SDL_WINDOW_RESIZABLE : SDL.SDL_WindowFlags.SDL_WINDOW_SHOWN;
renderWindow = SDL.SDL_CreateWindow(
Settings.DebugMode ? $"{Settings.Title} [DEBUG]" : Settings.Title, SDL.SDL_WINDOWPOS_CENTERED,
SDL.SDL_WINDOWPOS_CENTERED,
Settings.Resolution.Width,
Settings.Resolution.Height,
windowFlags);
}
void CreateRenderer()
{
if(Settings.VSinc) {
renderer = SDL.SDL_CreateRenderer(
renderWindow, -1,
SDL.SDL_RendererFlags.SDL_RENDERER_PRESENTVSYNC |
SDL.SDL_RendererFlags.SDL_RENDERER_ACCELERATED |
SDL.SDL_RendererFlags.SDL_RENDERER_TARGETTEXTURE
);
}
else
{
renderer = SDL.SDL_CreateRenderer(
renderWindow, -1,
SDL.SDL_RendererFlags.SDL_RENDERER_ACCELERATED |
SDL.SDL_RendererFlags.SDL_RENDERER_TARGETTEXTURE
);
}
}
The main cycle with the calculation of deltaTime:
void RenderLoop()
{
bool cap = Settings.FPS > 0;
var timer_fps = new Timer();
ulong now_counter = SDL.SDL_GetPerformanceCounter();
ulong last_counter = 0;
double deltaTime = 0;
while (renderLoop)
{
timer_fps.Start();
last_counter = now_counter;
now_counter = SDL.SDL_GetPerformanceCounter();
deltaTime = (double)(now_counter - last_counter) / SDL.SDL_GetPerformanceFrequency();
OnPreUpdate(deltaTime);
OnUpdate(deltaTime);
SDL.SDL_RenderPresent(renderer);
OnPostUpdate(deltaTime);
if( ( cap ) && ( timer_fps.GetTicks() < 1000 / Settings.FPS ) )
{
SDL.SDL_Delay( ( 1000 / Settings.FPS ) - timer_fps.GetTicks() );
}
}
}
Sprite loading and rendering code:
public Sprite(string path)
{
Path = path;
Transform = new Transform(new Point(0, 0));
sprite = Image.IMG_LoadTexture(Game.RenderContext, path);
SDL.SDL_QueryTexture(sprite, out format, out access, out width, out height); // get the width and height of the texture
draw_rect = new SDL.SDL_FRect();
draw_rect.w = width;
draw_rect.h = height;
scr_rect.x = 0; scr_rect.y = 0; scr_rect.w = width; scr_rect.h = height;
}
public Transform Transform
{
get; set;
}
public void Draw()
{
draw_rect.x = (float)Transform.Position.X;
draw_rect.y = (float)Transform.Position.Y;
SDL.SDL_RenderCopyExF(Game.RenderContext, sprite, ref scr_rect, ref draw_rect, Transform.Degrees, IntPtr.Zero, SDL.SDL_RendererFlip.SDL_FLIP_NONE);
}
Moving Function (Transform.Translate):
public void Translate(double x, double y)
{
position.X += x;
position.Y += y;
}
Parallax implementation function (move the sprites back to the right side when they leave the screen):
void layerDraw(Sprite l1, Sprite l2, double speed)
{
l1.Transform.Translate(speed, 0);
l1.Draw();
l2.Transform.Translate(speed, 0);
l2.Draw();
if (l1.Transform.Position.X <= -Settings.Resolution.Width)
l1.Transform.SetPosition(Settings.Resolution.Width + l2.Transform.Position.X, 0);
if (l2.Transform.Position.X <= -Settings.Resolution.Width)
l2.Transform.SetPosition(Settings.Resolution.Width + l1.Transform.Position.X, 0);
}
Parallax Rendering Function:
double speed_09 = -2.0, speed_08 = -4.0, speed_07 = -8.0, speed_06 = -16.0;
double speed_05 = -24.0, speed_04 = -32.0, speed_03 = -64.0, speed_02 = -96.0, speed_01 = -128.0;
protected override void Update(double deltaTime)
{
background.Draw();
layerDraw(forest_091, forest_092, speed_09 * deltaTime);
layerDraw(forest_081, forest_082, speed_08 * deltaTime);
layerDraw(forest_071, forest_072, speed_07 * deltaTime);
layerDraw(forest_061, forest_062, speed_06 * deltaTime);
layerDraw(particles051, particles052, speed_05 * deltaTime);
layerDraw(forest_041, forest_042, speed_04 * deltaTime);
layerDraw(particles_031, particles_032, speed_03 * deltaTime);
layerDraw(bushes_021, bushes_022, speed_02 * deltaTime);
layerDraw(mist_011, mist_012, speed_01 * deltaTime);
}
Outputting forest_091 coordinates to the console:
[11:31:07:32] (x,y): = (1902,7461329999965:0), deltaTime = 0,001391, speed = -0,002782
[11:31:07:32] (x:y): = (1902,7430913999965:0), deltaTime = 0,0015208, speed = -0,0030416
[11:31:07:32] (x:y): = (1902,7400399999965:0), deltaTime = 0,0015257, speed = -0,0030514
[11:31:07:32] (x:y): = (1902,7370409999965:0), deltaTime = 0,0014995, speed = -0,002999
[11:31:07:32] (x:y): = (1902,7339605999964:0), deltaTime = 0,0015402, speed = -0,0030804
[11:31:07:33] (x:y): = (1902,7300727999964:0), deltaTime = 0,0019439, speed = -0,0038878
[11:31:07:33] (x:y): = (1902,7271281999963:0), deltaTime = 0,0014723, speed = -0,0029446
[11:31:07:33] (x:y): = (1902,7241953999962:0), deltaTime = 0,0014664, speed = -0,0029328
[11:31:07:33] (x:y): = (1902,7212207999962:0), deltaTime = 0,0014873, speed = -0,0029746
[11:31:07:33] (x:y): = (1902,7181395999962:0), deltaTime = 0,0015406, speed = -0,0030812
[11:31:07:33] (x:y): = (1902,715346599996:0), deltaTime = 0,0013965, speed = -0,002793
[11:31:07:33] (x:y): = (1902,712221399996:0), deltaTime = 0,0015626, speed = -0,0031252
[11:31:07:34] (x:y): = (1902,709382799996:0), deltaTime = 0,0014193, speed = -0,0028386
Link to youtube with a demonstration of the problemы
I use nectcore3.1, SDL2-CS и SDL2 v2.0.10.0
UPD: log deltaTime calculations:
[08:48:34:25] now_counter = 1097310517365, last_counter = 1097310516099, now_counter-last_counter = 1266
[08:48:34:25] now_counter = 1097310519141, last_counter = 1097310517365, now_counter-last_counter = 1776
[08:48:34:25] now_counter = 1097310521406, last_counter = 1097310519141, now_counter-last_counter = 2265
[08:48:34:25] now_counter = 1097310532746, last_counter = 1097310521406, now_counter-last_counter = 11340
[08:48:34:25] now_counter = 1097310534069, last_counter = 1097310532746, now_counter-last_counter = 1323
[08:48:34:25] now_counter = 1097310535356, last_counter = 1097310534069, now_counter-last_counter = 1287
[08:48:34:25] now_counter = 1097310536628, last_counter = 1097310535356, now_counter-last_counter = 1272
[08:48:34:25] now_counter = 1097310537897, last_counter = 1097310536628, now_counter-last_counter = 1269
[08:48:34:25] now_counter = 1097310539169, last_counter = 1097310537897, now_counter-last_counter = 1272
[08:48:34:25] now_counter = 1097310540441, last_counter = 1097310539169, now_counter-last_counter = 1272`
Thanks to keltar for the answers and the time spent. In fact, I struggled for a long time to solve this problem and sincerely did not understand why even after adding the ability to draw floating-point sprites in SDL2, they still drew me according to the pixel grid of the monitor.
The good news: deltaTime is calculated correctly,
And there are no problems with rounding double when transferring coordinates to the SDL render!
Bad news: the solution is too simple to spend a few days on it.
SDL.SDL_SetHint (SDL.SDL_HINT_RENDER_SCALE_QUALITY, "2");
This line tells SDL2 to use Anisatropic filtering when rendering sprites.
It made my animation flow smoothly.
Related
Mouse movement based on head rotation
I've a device which gives a quaternion data about the direction the device is facing and I want to use this data to move the mouse on-screen. I've written the following code until now, but even when the device is idle (and I'm not getting major change in angle), I'm noticing mouse movement towards top-left Setting the next position: public void OnDataReceived(Quaternion quat) { var angle = GetAngle(quat.X, quat.Y, quat.Z, quat.W); angle.Z = 0f; var diff = angle - lastAngle; lastAngle = angle; var dtX = (int)(Math.Tan(diff.Y) * MoveMultiplier); var dtY = (int)(Math.Sin(diff.X) * MoveMultiplier); User32Wrapper.GetCursorPos(ref current); next.x = Math.Clamp(current.x + dtX, 0, Width); next.y = Math.Clamp(current.y + dtY, 0, Height); isDirty = true; } Moving mouse (which is being called continuously): private void MoveMouse(float deltaTime) { if (isDirty) { var dt = Speed * deltaTime; var x = (int)Lerp(current.x, next.x, dt); var y = (int)Lerp(current.y, next.y, dt); if (x >= 0 && x < Width && y >= 0 && y < Height) { current.x = x; current.y = y; User32Wrapper.Move(x, y); if (Math.Abs(current.x - next.x) < precision && Math.Abs(current.y - next.y) < precision) { isDirty = false; User32Wrapper.Move(next.x, next.y); User32Wrapper.GetCursorPos(ref current); } } } } User32Wrapper.Move() is call to win32's mouse_event() DLL_EXPORT void __cdecl Move(int x, int y) { int _x = x * 65535 / GetSystemMetrics(0); int _y = y * 65535 / GetSystemMetrics(1); mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, _x, _y, 0, 0); } Am I missing something with mouse movement, any help would be appreciated. Thanks
Win2D: Correct usage of CanvasVirtualControl
I want to draw the mandelbrot-set taken from the Win2D-Example-Gallery and tweak it a little. At first I had all my code to generate the mandelbrot inside the CreateResources-Method of CanvasAnimatedControl, but due to performance issues I went on to do it using shaders (HLSL or PixelShaderEffect) and CanvasVirtualControl: public PixelShaderEffect _effectMandel; CanvasVirtualImageSource _sdrc; public async Task CreateResources(CanvasVirtualControl sender) { _sdrc = new CanvasVirtualImageSource(sender, new Size(_width, _height)); var arr = await FileHelper.ReadAllBytes("Shaders/Mandelbrot.bin"); if (arr != null) { _effectMandel = new PixelShaderEffect(arr); using (CanvasDrawingSession drawingSession = sender.CreateDrawingSession(new Rect(0,0,_width,_height))) { drawingSession.DrawImage(_effectMandel); } } } When I run the application, I get a System.Runtime.InteropServices.COMException right in the using section and the 'App.g.i.cs' file opens up telling me: The shader code I use is this: // Copyright (c) Microsoft Corporation. All rights reserved. // // Licensed under the MIT License. See LICENSE.txt in the project root for license information. // This shader has no input textures. // It generates a mandelbrot fractal. #define D2D_INPUT_COUNT 0 #define D2D_REQUIRES_SCENE_POSITION #include "d2d1effecthelpers.hlsli" float scale; float2 translate; static const float4 tapOffsetsX = float4(-0.25, 0.25, -0.25, 0.25); static const float4 tapOffsetsY = float4(-0.25, -0.25, 0.25, 0.25); static const int iterations = 100; D2D_PS_ENTRY(main) { float2 pos = D2DGetScenePosition().xy; // Improve visual quality by supersampling inside the pixel shader, evaluating four separate // versions of the fractal in parallel, each at a slightly different position offset. // The x, y, z, and w components of these float4s contain the four simultaneous computations. float4 c_r = (pos.x + tapOffsetsX) * scale + translate.x; float4 c_i = (pos.y + tapOffsetsY) * scale + translate.y; float4 value_r = 0; float4 value_i = 0; // Evalulate the Mandelbrot fractal. for (int i = 0; i < iterations; i++) { float4 new_r = value_r * value_r - value_i * value_i + c_r; float4 new_i = value_r * value_i * 2 + c_i; value_r = new_r; value_i = new_i; } // Adjust our four parallel results to range 0:1. float4 distanceSquared = value_r * value_r + value_i * value_i; float4 vectorResult = isfinite(distanceSquared) ? saturate(1 - distanceSquared) : 0; // Resolve the supersampling to produce a single scalar result. float result = dot(vectorResult, 0.25); if (result < 1.0 / 256) return 0; else return float4(result, result, result, 1); } If you know why this happens, please answer. Thanks!
I needed to setup a Timer to regularly invalidate the canvas and get 60fps. I had another look into the Microsoft Examples and finally worked it out using this code: DispatcherTimer timer; internal void Regions_Invalidated(CanvasVirtualControl sender, CanvasRegionsInvalidatedEventArgs args) { // Configure the Mandelbrot effect to position and scale its output. float baseScale = 0.005f; float scale = (baseScale * 96 / sender.Dpi) / (helper._modifiers[1] / 1000f); var controlSize = baseScale * sender.Size.ToVector2() * scale; Vector2 translate = (baseScale * sender.Size.ToVector2() * new Vector2(-0.5f,-0f)); _effectMandel.Properties["scale"] = scale; _effectMandel.Properties["translate"] = (Microsoft.Graphics.Canvas.Numerics.Vector2)translate; #endif // Draw the effect to whatever regions of the CanvasVirtualControl have been invalidated. foreach (var region in args.InvalidatedRegions) { using (var drawingSession = sender.CreateDrawingSession(region)) { drawingSession.DrawImage(_effectMandel); } } // start timer for fps this.timer = new DispatcherTimer(); int fps = 60; this.timer.Interval = new TimeSpan(0, 0, 0, 0, 100 / fps); this.timer.Tick += timer_Tick; this.timer.Start(); } private void timer_Tick(object sender, object e) { this.timer.Stop(); _canvas.Invalidate(); } Hope this is helpful to someone.
How to scale a CCSprite to any resolution in CocosSharp (Android)?
I'm trying to scale a CCSprite to any resolution in CocosSharp, this is what I've got: void AddTruck () { var spriteSheet = new CCSpriteSheet ("animations/truck.plist"); var animationFrames = spriteSheet.Frames.FindAll ((x) => x.TextureFilename.StartsWith ("frame")); walkAnim = new CCAnimation (animationFrames, 0.1f); walkRepeat = new CCRepeatForever (new CCAnimate (walkAnim)); truck = new CCSprite (animationFrames.First ()) { Name = "Truck" }; truck.Scale = 0.70f; AddChild (truck); } And I want that when its added to scene, it be resized according to the device resolution... Any tips? Thanks.
Well, I think that in order to do that, you have to have another folder with HD images, and acording to the device resolution, you use 'em... But I did the following, and it worked for me: float ResTruck(CCSprite sprite) { float scale = 0.0f; float resWid = CCScene.DefaultDesignResolutionSize.Width; float resHei = CCScene.DefaultDesignResolutionSize.Height; if (resWid > 500) scale = 0.70f; else if (resWid > 1080) scale = 0.90f; else if (resWid > 1300) scale = 1.0f; return scale; } And then, I asing the "scale" value to my sprite: float scale = ResTruck(truck); truck.Scale = scale; And that's it :v
Point-Zoom on Mandelbrot Set in C# - It works, except when the mouse has moved
I'm able to point zoom on the Mandelbrot set, as long as the mouse doesn't move after zooming has begun. I've tried calculating a normalized delta (new coordinate - old coordinate)*(oldzoom), but what happens is the image appears to jump around to a new location. I've seen this issue before. I'm struggling more here because I have to somehow convert this mouse position delta back to the -2,2 coordinate space of the Mandelbrot set. Here's my code. What's important is the GetZoomPoint method, and then the lines of code that define x0 and y0. Also, I use the Range class to scale values from one range to another. I WAS using deltaTrans (thats the thing I was talking about earlier where I normalize the mouse delta with the old scale). using OpenTK.Graphics.OpenGL; using SpriteSheetMaker; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Fractal.Fractal { public class Mandelbrot : BaseTexture { private static Transform GlobalTransform = SpriteSheetMaker.Global.Transform; private static Vector3 GlobalScale = GlobalTransform.Scale; private static Vector3 GlobalTrans = GlobalTransform.Translation; private static Vector3 LastWindowPoint = null; private static Vector3 ZoomFactor = Vector3.ONE * 1.2f; private static Vector3 Displacement = Vector3.ZERO; private static int WindowSize = 100; public static Vector3 GetZoomPoint() { var zP = OpenGLHelpers.LastZoomPoint.Clone(); if (LastWindowPoint == null) { LastWindowPoint = zP.Clone(); } var delta = zP - LastWindowPoint; var oldZoom = GlobalScale / ZoomFactor; var deltaTrans = delta.XY * oldZoom.XY; var factor = ZoomFactor.Clone(); Range xR = new Range(0, WindowSize); Range yR = new Range(0, WindowSize); Range complexRange = new Range(-2, 2); // Calculate displacement of zooming position. var dx = (zP.X - Displacement.X) * (factor.X - 1f); var dy = (zP.Y - Displacement.Y) * (factor.Y - 1f); // Compensate for displacement. Displacement.X -= dx; Displacement.Y -= dy; zP -= Displacement; var x = complexRange.ScaleValue(zP.X, xR); var y = complexRange.ScaleValue(zP.Y, yR); var rtn = new Vector3(x, y); LastWindowPoint = zP.Clone(); return rtn; } public static Mandelbrot Generate() { var size = new Size(WindowSize, WindowSize); var radius = new Size(size.Width / 2, size.Height / 2); Bitmap bmp = new Bitmap(size.Width, size.Height); LockBitmap.LockBitmapUnsafe lbm = new LockBitmap.LockBitmapUnsafe(bmp); lbm.LockBits(); var pt = Mandelbrot.GetZoomPoint(); Parallel.For(0, size.Width, i => { // float x0 = complexRangeX.ScaleValue(i, xRange); float x0 = ((i - radius.Width) / GlobalScale.X) + pt.X; Parallel.For(0, size.Height, j => { // float y0 = complexRangeY.ScaleValue(j, yRange); float y0 = ((j - radius.Height) / GlobalScale.Y) + pt.Y; float value = 0f; float x = 0.0f; float y = 0.0f; int iteration = 0; int max_iteration = 100; while (x * x + y * y <= 4.0 && iteration < max_iteration) { float xtemp = x * x - y * y + x0; y = 2.0f * x * y + y0; x = xtemp; iteration += 1; if (iteration == max_iteration) { value = 255; break; } else { value = iteration * 50f % 255f; } } int v = (int)value; lbm.SetPixel(i, j, new ColorLibrary.HSL(v / 255f, 1.0, 0.5).ToDotNetColor()); }); }); lbm.UnlockBits(); var tex = new BaseTextureImage(bmp); var rtn = new Mandelbrot(tex); return rtn; } public override void Draw() { base._draw(); } private Mandelbrot(BaseTextureImage graphic) { var topLeft = new Vector3(0, 1); var bottomLeft = new Vector3(0, 0); var bottomRight = new Vector3(1, 0); var topRight = new Vector3(1, 1); this.Vertices = new List<Vector3>() { topLeft,bottomLeft,bottomRight,topRight }; this.Size.X = WindowSize; this.Size.Y = WindowSize; this.Texture2D = graphic; } } }
I refactored my code, and also figured out a solution to this problem. 2 big wins in one. Ok, so I found a solution on CodeProject written in C# which I was readily able to adapt to my project. I'm not sure why I didn't realize this when I posted the question, but what I needed to solve this issue was to create a 'window' of zoom and not think in terms of a 'point zoom'. Yes, even if I am trying to zoom directly into a point, that point is just the center of some sort of a window. Here is the method I have, which expects start and end mousedown coordinates (screen space), and converts the mandelbrot set window size accordingly. public void ApplyZoom(double x0, double y0, double x1, double y1) { if (x1 == x0 && y0 == y1) { //This was just a click, no movement occurred return; } /* * XMin, YMin and XMax, YMax are the current extent of the set * mx0,my0 and mx1,my1 are the part we selected * do the math to draw the selected rectangle * */ double scaleX, scaleY; scaleX = (XMax - XMin) / (float)BitmapSize; scaleY = (YMax - YMin) / (float)BitmapSize; XMax = (float)x1 * scaleX + XMin; YMax = (float)y1 * scaleY + YMin; XMin = (float)x0 * scaleX + XMin; YMin = (float)y0 * scaleY + YMin; this.Refresh(); // force mandelbrot to redraw } Basically, whats happening is we calculate the ratio between the mandelbrot window size versus the screen size we are drawing to. Then, using that scale, we basically convert our mousedown coordinates to mandelbrot set coordinates (x1*scaleX, etc) and manipulate the current Min and Max coordinates with them, using the Min values as the pivot point. Here's the link to the CodeProject I used as a reference: CodeProject link
How To Make 3D Script Work With 2D (Unity C#)
So I have a script that shoots an arrow when you click and drag, kinda like Angry Birds. I want it to work with the 2D RigidBody and 2D collider but when I change the rigidbody.AddForce to rigidbody2D.AddForce, It doesn't work. How can I fix this to work for 2D? I also want the arrow to rotate in 2D space either up or down depending on where mouse is pulled back. When I try the mouse look script, it rotates it in the z axis (I think) and distorts the arrow. Any easy solution to fix this?? Thanks guys. I'm new to game making and I've been trying to figure this stuff out for like the last 10 hours. I need some pros to help! Thanks!!! Heres my script using UnityEngine; using System.Collections; public class DragShotMover2 : MonoBehaviour { public float maxDragLength = 2; // this is the base magnitude and the maximum length of the line drawn in the user interface public float maxMultiplier = 5; // multiply the line length by this to allow for higher force values to be represented by shorter lines public Vector3 dragPlaneNormal = Vector3.up; // a vector describing the orientation of the drag plan relative to world-space but centered on the target public SnapDir snapDirection = SnapDir.away; // force is applied either toward or away from the mouse on release public ForceMode forceTypeToApply = ForceMode.VelocityChange; public bool overrideVelocity = true; // cancel the existing velocity before applying the new force public bool pauseOnDrag = true; // causes the simulation to pause when the object is clicked and unpause when released public Color noForceColor = Color.yellow; // color of the visualization helpers at force 0 public Color maxForceColor = Color.red; // color of the visualization helpers at maximum force public enum SnapDir {toward, away} private Vector3 forceVector; private float magPercent = 0; private bool mouseDragging = false; private Vector3 mousePos3D; private float dragDistance; private Plane dragPlane; private Ray mouseRay; private GameObject dragZone; private string shaderString = "Transparent/Diffuse"; private Material dzMat; void Start (){ Color currentColor = noForceColor; dzMat = new Material(Shader.Find(shaderString)); // create the dragzone visual helper dragZone = new GameObject("dragZone_" + gameObject.name); dragZone.AddComponent<MeshFilter>().mesh = MakeDiscMeshBrute(maxDragLength/4); //dragZone.GetComponent.MeshFilter. dragZone.AddComponent<MeshRenderer>(); dragZone.renderer.enabled = false; dragZone.name = "dragZone_" + gameObject.name; dragZone.transform.localScale = new Vector3(maxDragLength*2, 0.025f, maxDragLength*2); dragZone.renderer.material = dzMat; dragZone.renderer.material.color = currentColor * new Color(1,1,1,0.2f); // create the dragplane dragPlane = new Plane(dragPlaneNormal, transform.position); // orient the drag plane if (dragPlaneNormal != Vector3.zero) { dragZone.transform.rotation = Quaternion.LookRotation(dragPlaneNormal) * new Quaternion(1, 0, 0, 1); } else Debug.LogError("Drag plane normal cannot be equal to Vector3.zero."); //update the position of the dragzone dragZone.transform.position = transform.position; } void OnMouseDown (){ mouseDragging = true; if (pauseOnDrag) { // pause the simulation Time.timeScale = 0; } // update the dragplane dragPlane = new Plane(dragPlaneNormal, transform.position); // orient the drag plane if (dragPlaneNormal != Vector3.zero) { dragZone.transform.rotation = Quaternion.LookRotation(dragPlaneNormal) * new Quaternion(1, 0, 0, 1); } else Debug.LogError("Drag plane normal cannot be equal to Vector3.zero."); //update the position of the dragzone dragZone.transform.position = transform.position; dragZone.renderer.enabled = true; } void OnMouseDrag (){ Color currentColor = noForceColor; // update the plane if the target object has left it if (dragPlane.GetDistanceToPoint(transform.position) != 0) { // update dragplane by constructing a new one -- I should check this with a profiler dragPlane = new Plane(dragPlaneNormal, transform.position); } // create a ray from the camera, through the mouse position in 3D space mouseRay = Camera.main.ScreenPointToRay(Input.mousePosition); // if mouseRay intersects with dragPlane float intersectDist = 0.0f; if (dragPlane.Raycast(mouseRay, out intersectDist)) { // update the world space point for the mouse position on the dragPlane mousePos3D = mouseRay.GetPoint(intersectDist); // calculate the distance between the 3d mouse position and the object position dragDistance = Mathf.Clamp((mousePos3D - transform.position).magnitude, 0, maxDragLength); // calculate the force vector if (dragDistance*maxMultiplier < 1) dragDistance = 0; // this is to allow for a "no move" buffer close to the object forceVector = mousePos3D - transform.position; forceVector.Normalize(); forceVector *= dragDistance * maxMultiplier; // update color the color // calculate the percentage value of current force magnitude out of maximum magPercent = (dragDistance * maxMultiplier) / (maxDragLength * maxMultiplier); // choose color based on how close magPercent is to either 0 or max currentColor = noForceColor * (1-magPercent) + maxForceColor * magPercent; // dragzone color dragZone.renderer.material.color = currentColor * new Color(1,1,1,0.2f); // draw the line Debug.DrawRay(transform.position, forceVector / maxMultiplier, currentColor); } //update the position of the dragzone dragZone.transform.position = transform.position; } void OnMouseUp (){ mouseDragging = false; if (overrideVelocity) { // cancel existing velocity rigidbody.AddForce(-rigidbody.velocity, ForceMode.VelocityChange); } // add new force int snapD = 1; if (snapDirection == SnapDir.away) snapD = -1; // if snapdirection is "away" set the force to apply in the opposite direction rigidbody.AddForce(snapD * forceVector, forceTypeToApply); // cleanup dragZone.renderer.enabled = false; if (pauseOnDrag) { // un-pause the simulation Time.timeScale = 1; } } void OnGUI (){ if (mouseDragging) { Vector2 guiMouseCoord = GUIUtility.ScreenToGUIPoint(Input.mousePosition); GUI.Box ( new Rect(guiMouseCoord.x-30, Screen.height-guiMouseCoord.y+15, 100, 20), "force: "+Mathf.Round((forceVector).magnitude)); } } Mesh MakeDiscMeshBrute ( float r ){ Mesh discMesh; Vector3[] dmVerts = new Vector3[18]; Vector3[] dmNorms = new Vector3[18]; Vector2[] dmUVs = new Vector2[18]; int[] dmTris = new int[48]; int i = 0; discMesh = new Mesh(); dmVerts[0] = new Vector3(0,0,0); dmVerts[1] = new Vector3(0,0,r); dmVerts[2] = new Vector3(1,0,1).normalized * r; // find the vector at the correct distance the hacky-hillbilly way! dmVerts[3] = new Vector3(r,0,0); dmVerts[4] = new Vector3(1,0,-1).normalized * r; dmVerts[5] = new Vector3(0,0,-r); dmVerts[6] = new Vector3(-1,0,-1).normalized * r; dmVerts[7] = new Vector3(-r,0,0); dmVerts[8] = new Vector3(-1,0,1).normalized * r; // set the other side to the same points for (i = 0; i<dmVerts.Length/2; i++) { dmVerts[dmVerts.Length/2 + i] = dmVerts[i]; } for (i = 0; i<dmNorms.Length; i++) { if (i<dmNorms.Length/2) dmNorms[i] = Vector3.up; // set side one to face up else dmNorms[i] = -Vector3.up; // set side two to face down } dmUVs[0] = new Vector2(0,0); dmUVs[1] = new Vector2(0,r); dmUVs[2] = new Vector2(1,1).normalized * r;; dmUVs[3] = new Vector2(r,0); dmUVs[4] = new Vector2(1,-1).normalized * r;; dmUVs[5] = new Vector2(0,-r); dmUVs[6] = new Vector2(-1,-1).normalized * r;; dmUVs[7] = new Vector2(-r,0); dmUVs[8] = new Vector2(-1,1).normalized * r;; // set the other side to the same points for (i = 0; i<dmUVs.Length/2; i++) { dmUVs[dmUVs.Length/2 + i] = dmUVs[i]; } dmTris[0] = 0; dmTris[1] = 1; dmTris[2] = 2; dmTris[3] = 0; dmTris[4] = 2; dmTris[5] = 3; dmTris[6] = 0; dmTris[7] = 3; dmTris[8] = 4; dmTris[9] = 0; dmTris[10] = 4; dmTris[11] = 5; dmTris[12] = 0; dmTris[13] = 5; dmTris[14] = 6; dmTris[15] = 0; dmTris[16] = 6; dmTris[17] = 7; dmTris[18] = 0; dmTris[19] = 7; dmTris[20] = 8; dmTris[21] = 0; dmTris[22] = 8; dmTris[23] = 1; // side two dmTris[24] = 9; dmTris[25] = 11; dmTris[26] = 10; dmTris[27] = 9; dmTris[28] = 12; dmTris[29] = 11; dmTris[30] = 9; dmTris[31] = 13; dmTris[32] = 12; dmTris[33] = 9; dmTris[34] = 14; dmTris[35] = 13; dmTris[36] = 9; dmTris[37] = 15; dmTris[38] = 14; dmTris[39] = 9; dmTris[40] = 16; dmTris[41] = 15; dmTris[42] = 9; dmTris[43] = 17; dmTris[44] = 16; dmTris[45] = 9; dmTris[46] = 10; dmTris[47] = 17; discMesh.vertices = dmVerts; discMesh.uv = dmUVs; discMesh.normals = dmNorms; discMesh.triangles = dmTris; return discMesh; } }
If you want to keep using the 3D Rigidbody, I'd suggest just using RigidbodyConstraints, so you can lock the z (or whatever) axis/rotation, and it will perform exactly the same as a 2D platformer.