Draw a closed circle with LineRenderer - c#

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System;
[RequireComponent(typeof(LineRenderer))]
public class DrawCircle : MonoBehaviour
{
[Range(0, 50)]
public int segments = 50;
public bool circle = true;
[Range(0, 50)]
public float xradius = 15;
[Range(0, 50)]
public float yradius = 15;
LineRenderer line;
public float drawSpeed = 0.3f;
void Start()
{
line = gameObject.GetComponent<LineRenderer>();
line.positionCount = (segments + 1);
line.useWorldSpace = false;
StartCoroutine(CreatePoints());
}
IEnumerator CreatePoints()
{
if (circle == true)
yradius = xradius;
float x;
float z;
float change = 2 * (float)Math.PI / segments;
float angle = change;
x = Mathf.Sin(angle) * xradius;
line.SetPosition(0, new Vector3(x, 0.5f, 0));
for (int i = 1; i < (segments); i++)
{
x = Mathf.Sin(angle) * xradius;
z = Mathf.Cos(angle) * yradius;
yield return new WaitForSeconds(drawSpeed);
line.SetPosition((int)i, new Vector3(x, 0.5f, z));
angle += change;
}
}
}
When running the game I want it will start with one line at index 0 drawn already from the center of the circle. So I added this two lines before the loop:
x = Mathf.Sin(angle) * xradius;
line.SetPosition(0, new Vector3(x, 0.5f, 0));
Before adding this two lines the for loop was:
for (int i = 0; i < (segments + 1); i++)
But now I want to start the loop from index 1 so I tried:
for (int i = 1; i < (segments); i++)
But it's not making a complete round:
The circle drawing is moving in the clockwise direction.

You're not starting at 0
float angle = change; //change is > 0
I'll assume change is 10 degrees for the purposes of explanation.
Because of this, your first segment goes from the center, to a point 10 degrees around the circle.
You should do it like this:
float angle = 0;
x = Mathf.Sin(angle) * xradius;
line.SetPosition(0, new Vector3(x, 0.5f, 0));

Related

Sphere are overlapping even tho I check for collisions

I am using this code to generate a number of sphere around a center point, with different size and position.
There is a conditional check to prevent sphere from overlapping.
The problem I face is, in the scene some sphere are always overlapping.
=>
[Header("Galaxy")]
public int numberOfStars = 300;
public int maximumRadius = 100;
public float minDistBetweenStars = 2f;
[Header("Star")]
public float minimumSize = 1f;
public float maximumSize = 2f;
void Start()
{
for(int i = 0; i < numberOfStars; i++)
{
float distance = Random.Range(0, maximumRadius);
float angle = Random.Range(0, 2 * Mathf.PI);
float starSize = Random.Range(minimumSize, maximumSize);
Vector3 starPosition = new Vector3(distance * Mathf.Cos(angle), 0, distance * Mathf.Sin(angle));
Collider[] starCollider = Physics.OverlapSphere(starPosition, (starSize * 0.5f) + minDistBetweenStars);
if (starCollider.Length == 0)
{
GameObject star = GameObject.CreatePrimitive(PrimitiveType.Sphere);
star.transform.position = starPosition;
star.transform.localScale = new Vector3(starSize, starSize, starSize);
}
else
{
i--;
}
}
}
I do pass in the remove, so some sphere are being removed,m but I still see a lot of overlapping.
So the problem was we were not disabling the collider on the sphere we was checking :P
public class StarGenSO : MonoBehaviour
{
[Header("Galaxy")]
public int numberOfStars = 300;
public int maximumRadius = 100;
public float minDistBetweenStars = 2f;
[Header("Star")]
public float minimumSize = 1f;
public float maximumSize = 2f;
void Awake()
{
for (int i = 0; i < numberOfStars; i++)
{
GameObject star = GameObject.CreatePrimitive(PrimitiveType.Sphere);
float distance = Random.Range(0, maximumRadius);
float angle = Random.Range(0, 2 * Mathf.PI);
float starSize = Random.Range(minimumSize, maximumSize);
Vector3 starPosition = new Vector3(distance * Mathf.Cos(angle), 0, distance * Mathf.Sin(angle));
var currentCol = star.GetComponent<Collider>();
currentCol.enabled = false;
Collider[] starCollider = Physics.OverlapSphere(starPosition, (starSize * 0.5f) + minDistBetweenStars);
if (starCollider.Length == 0)
{
star.transform.position = starPosition;
star.transform.localScale = new Vector3(starSize, starSize, starSize);
currentCol.enabled = true;
}
else
{
Debug.Log("remove");
Destroy(star);
i--;
}
}
}
}
A preferred solution
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class StarGenSo : MonoBehaviour
{
[Header("Galaxy")]
public int numberOfStars = 300;
public int maximumRadius = 100;
public float minDistBetweenStars = 2f;
[Header("Star")]
public float minimumSize = 1f;
public float maximumSize = 2f;
[Header("Star Object")]
public GameObject obj;
IEnumerator Start()
{
for (int i = 0; i < numberOfStars; i++)
{
float distance = Random.Range(0, maximumRadius);
float angle = Random.Range(0, 2 * Mathf.PI);
float starSize = Random.Range(minimumSize, maximumSize);
Vector3 starPosition = new Vector3(distance * Mathf.Cos(angle), 0, distance * Mathf.Sin(angle));
Collider[] starCollider = Physics.OverlapSphere(starPosition, (starSize * 0.5f) + minDistBetweenStars);
if (starCollider.Length == 0)
{
GameObject star = Instantiate(obj);
star.transform.position = starPosition;
star.transform.localScale = new Vector3(starSize, starSize, starSize);
star.AddComponent<SphereCollider>();
}
else
{
i--;
}
}
yield return null;
}
}
Result:
The problem here is your starSize use both for local scale (multiple factor) and sphere collider (flat factor). Eg: your star have default size of 2 and your starSize equal 2, then your actual radius of your sphere is 4, but your collider check for 2*0.5 + 2 = 3.

Create just one sphere that travels between two points in a loop within a half circle?

In relation to the question asked here (How to place spheres in a half circle shape between 2 points) that generates spheres between two points A and B.
How do I create just one sphere that moves from Point A to Point B and then back from Point B to Point A in a loop cycle? How do I use Lerp in this context?
I have tried making the sphere move in the angle (half circle) described in the below code but it always moves in a straight line.
The below code generates spheres between two points.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GetCurves : MonoBehaviour
{
public GameObject A;
public GameObject B;
public int amount;
[ContextMenu("PlaceSpheres()")]
public void Start()
{
PlaceSpheres(A.transform.position, B.transform.position, amount);
}
public void PlaceSpheres(Vector3 posA, Vector3 posB, int numberOfObjects)
{
// get circle center and radius
var radius = Vector3.Distance(posA, posB) / 2f;
var centerPos = (posA + posB) / 2f;
// get a rotation that looks in the direction
// posA -> posB
var centerDirection = Quaternion.LookRotation((posB - posA).normalized);
for (var i = 0; i < numberOfObjects; i++)
{
var angle = Mathf.PI * (i+1) / (numberOfObjects + 1); //180 degrees
var x = Mathf.Sin(angle) * radius;
var z = Mathf.Cos(angle) * radius;
var pos = new Vector3(x, 0, z);
// Rotate the pos vector according to the centerDirection
pos = centerDirection * pos;
var sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
sphere.transform.position = centerPos + pos;
sphere.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
}
}
}
The below script I had created that makes an object move between two points in a loop but only in a straight line. How do I make it move in a curve (180 degrees)?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RunInLoop : MonoBehaviour
{
public float speed = 0.25f;
public Transform PointA;
public Transform PointB;
private Vector3 origin;
private bool backToOrigin;
void Start()
{
transform.position = PointA.transform.position;
origin = transform.position;
}
void Update()
{
transform.position = Vector3.MoveTowards(transform.position, backToOrigin ? origin : PointB.transform.position, speed * Time.deltaTime);
// if one of the two positions is reached invert the flag
if (transform.position == PointB.transform.position || transform.position == origin)
{
backToOrigin = !backToOrigin;
}
}
}
Solution using your code
As I told you in my last answer that provided your first code you should store them in a list and then make the object move between them:
public class GetCurves : MonoBehaviour
{
public GameObject A;
public GameObject B;
public int amount;
public float moveSpeed;
private List<Vector3> positions = new List<Vector3>();
private Transform sphere;
private int currentIndex = 0;
private bool movingForward = true;
private void Start()
{
sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere).transform;
sphere.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
GeneratePositions(A.transform.position, B.transform.position, amount);
sphere.position = positions[0];
}
private void GeneratePositions(Vector3 posA, Vector3 posB, int numberOfObjects)
{
// get circle center and radius
var radius = Vector3.Distance(posA, posB) / 2f;
var centerPos = (posA + posB) / 2f;
// get a rotation that looks in the direction
// posA -> posB
var centerDirection = Quaternion.LookRotation((posB - posA).normalized);
for (var i = 0; i < numberOfObjects; i++)
{
var angle = Mathf.PI * (i + 1) / (numberOfObjects + 1); //180 degrees
var x = Mathf.Sin(angle) * radius;
var z = Mathf.Cos(angle) * radius;
var pos = new Vector3(x, 0, z);
// Rotate the pos vector according to the centerDirection
pos = centerDirection * pos;
// store them in a list this time
positions.Add(centerPos + pos);
}
}
private void Update()
{
if (positions == null || positions.Count == 0) return;
// == for Vectors works with precision of 0.00001
// if you need a better precision instead use
//if(!Mathf.Approximately(Vector3.Distance(sphere.position, positions[currentIndex]), 0f))
if (sphere.position != positions[currentIndex])
{
sphere.position = Vector3.MoveTowards(sphere.transform.position, positions[currentIndex], moveSpeed * Time.deltaTime);
return;
}
// once the position is reached select the next index
if (movingForward)
{
if (currentIndex + 1 < positions.Count)
{
currentIndex++;
}
else if (currentIndex + 1 >= positions.Count)
{
currentIndex--;
movingForward = false;
}
}
else
{
if (currentIndex - 1 >= 0)
{
currentIndex--;
}
else
{
currentIndex++;
movingForward = true;
}
}
}
}
If you want to stick to Single-Responsibility-Principles you could also seperate the movement from the list generation like e.g.
public class GetCurves : MonoBehaviour
{
public GameObject A;
public GameObject B;
public int amount;
public float moveSpeed;
private void Start()
{
GeneratePositions(A.transform.position, B.transform.position, amount);
}
private void GeneratePositions(Vector3 posA, Vector3 posB, int numberOfObjects)
{
// get circle center and radius
var radius = Vector3.Distance(posA, posB) / 2f;
var centerPos = (posA + posB) / 2f;
// get a rotation that looks in the direction
// posA -> posB
var centerDirection = Quaternion.LookRotation((posB - posA).normalized);
List<Vector3> positions = new List<Vector3>();
for (var i = 0; i < numberOfObjects; i++)
{
var angle = Mathf.PI * (i + 1) / (numberOfObjects + 1); //180 degrees
var x = Mathf.Sin(angle) * radius;
var z = Mathf.Cos(angle) * radius;
var pos = new Vector3(x, 0, z);
// Rotate the pos vector according to the centerDirection
pos = centerDirection * pos;
// store them in a list this time
positions.Add(centerPos + pos);
}
var sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
sphere.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
var movement = sphere.AddComponent<MoveBetweenPoints>();
movement.positions = positions;
movement.moveSpeed = moveSpeed;
}
and in a seperate script
public class MoveBetweenPoints : MonoBehaviour
{
public List<Vector3> positions = new List<Vector3>();
public float moveSpeed;
privtae bool movingForward = true;
private int currentIndex = 0;
private void Update()
{
if (positions == null || positions.Count == 0) return;
// == for Vectors works with precision of 0.00001
// if you need a better precision instead use
//if(!Mathf.Approximately(Vector3.Distance(sphere.position, positions[currentIndex]), 0f))
if (sphere.position != positions[currentIndex])
{
transform.position = Vector3.MoveTowards(transform.position, positions[currentIndex], moveSpeed * Time.deltaTime);
return;
}
// once the position is reached select the next index
if (movingForward)
{
if (currentIndex + 1 < positions.Count)
{
currentIndex++;
}
else if (currentIndex + 1 >= positions.Count)
{
currentIndex--;
movingForward = false;
}
}
else
{
if (currentIndex - 1 >= 0)
{
currentIndex--;
}
else
{
currentIndex++;
movingForward = true;
}
}
}
}
Actual Solution
However, if you want a smooth movement on a circle curve ... why even reduce that circle curcve to a certain amount of positions? You could directly moove according to the angle between 0° and 180° like this:
public class GetCurves : MonoBehaviour
{
public GameObject A;
public GameObject B;
// now in Angles per second
public float moveSpeed;
private Transform sphere;
private bool movingForward = true;
private float angle;
private void Start()
{
sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere).transform;
sphere.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
}
private void Update()
{
if (movingForward)
{
angle += moveSpeed * Time.deltaTime;
}
else
{
angle -= moveSpeed * Time.deltaTime;
}
if (angle < 0)
{
angle = 0;
movingForward = true;
}
else if (angle > 180)
{
angle = 180;
movingForward = false;
}
// get circle center and radius
var radius = Vector3.Distance(A.transform.position, B.transform.position) / 2f;
var centerPos = (A.transform.position + B.transform.position) / 2f;
// get a rotation that looks in the direction
// posA -> posB
var centerDirection = Quaternion.LookRotation((B.transform.position - A.transform.position).normalized);
var x = Mathf.Sin(angle * Mathf.Deg2Rad) * radius;
var z = Mathf.Cos(angle * Mathf.Deg2Rad) * radius;
var pos = new Vector3(x, 0, z);
// Rotate the pos vector according to the centerDirection
pos = centerDirection * pos;
sphere.position = centerPos + pos;
}
}

Unity smooth camera zooming Rts game

Hello so i have a problem my camera is really sanping and i want it to zoom in and out smooth i looked at videos and forums but i dont know how can i get help this is the code for all the camera its for a rts game and all it does for now is you can zoom in out move the camera with your mouse being on the edges on the screen.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RtsCamera : MonoBehaviour {
public float scrollZone = 16;
public float scrollSpeed = 5;
public float zoomSensitivy = 15.0f;
public float xMax = 8;
public float xMin = 0;
public float yMax = 8;
public float yMin = 0;
public float zMax = 8;
public float zMin = 0;
private Vector3 desiredPosition;
private void Start()
{
desiredPosition = transform.position;
}
// Update is called once per frame
void Update () {
float x = 0, y = 0, z = 0;
float speed = scrollZone * Time.deltaTime;
if (Input.mousePosition.x < scrollZone)
x -= speed;
else if (Input.mousePosition.x > Screen.width - scrollZone)
x += speed;
if (Input.mousePosition.y < scrollZone)
z -= speed;
else if (Input.mousePosition.y > Screen.height - scrollZone)
z += speed;
y += Input.GetAxis("Mouse ScrollWheel")* zoomSensitivy;
Vector3 move = new Vector3(x,y,z) + transform.position;
move.x = Mathf.Clamp(move.x ,xMin ,xMax);
move.y = Mathf.Clamp(move.y ,yMin ,yMax);
move.z = Mathf.Clamp(move.z ,zMin ,zMax);
desiredPosition = move;
transform.position = Vector3.Lerp(transform.position, desiredPosition, 0.2f);
}
}

How can i make equal spaces between the squad members in the circle formation?

When it's square formation i can set the space fine.
But i'm not sure how to do it with the circle formation.
Inside the FormationSquare method i'm using the space variable for the square formation. Now i need to do the same idea for the circle formation too.
Maybe inside the RandomCircle to change something and using the space variable ?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SquadFormation : MonoBehaviour
{
enum Formation
{
Square, Circle
}
public Transform squadMemeber;
public int columns = 4;
public int space = 10;
public int numObjects = 20;
public float yOffset = 1;
private Formation formation;
// Use this for initialization
void Start()
{
formation = Formation.Square;
ChangeFormation();
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.F))
{
GameObject[] objects = GameObject.FindGameObjectsWithTag("Squad Member");
if (objects.Length > 0)
{
foreach (GameObject obj in objects)
Destroy(obj);
}
ChangeFormation();
}
}
private void ChangeFormation()
{
switch (formation)
{
case Formation.Square:
for (int i = 0; i < 23; i++)
{
Transform go = Instantiate(squadMemeber);
Vector3 pos = FormationSquare(i);
go.position = new Vector3(transform.position.x + pos.x, 0, transform.position.y + pos.y);
go.Rotate(new Vector3(0, -90, 0));
go.tag = "Squad Member";
}
formation = Formation.Circle;
break;
case Formation.Circle:
Vector3 center = transform.position;
for (int i = 0; i < numObjects; i++)
{
Vector3 pos = RandomCircle(center, 5.0f);
var rot = Quaternion.LookRotation(center - pos);
pos.y = Terrain.activeTerrain.SampleHeight(pos);
pos.y = pos.y + yOffset;
Transform insObj = Instantiate(squadMemeber, pos, rot);
insObj.rotation = rot;
insObj.tag = "Squad Member";
}
formation = Formation.Square;
break;
}
}
Vector2 FormationSquare(int index) // call this func for all your objects
{
float posX = (index % columns) * space;
float posY = (index / columns) * space;
return new Vector2(posX, posY);
}
Vector3 RandomCircle(Vector3 center, float radius)
{
float ang = Random.value * 360;
Vector3 pos;
pos.x = center.x + radius * Mathf.Sin(ang * Mathf.Deg2Rad);
pos.z = center.z + radius * Mathf.Cos(ang * Mathf.Deg2Rad);
pos.y = center.y;
return pos;
}
}
The issue here is that your RandomCircle() method is...well, random. You're just arbitrarily grabbing an angle around the circle, without any regard for where other squad members have been placed, meaning they'll practically never be evenly distributed.
Consider calculating the angle that will separate each squad member (if distributed evenly) beforehand, then perform a similar approach to FormationSquare() where you iterate through the squad to distribute them.
This may be a bit closer to what you want (basing this on the assumption that RandomCircle() already works as designed):
Vector3 FormationCircle(Vector3 center, float radius, int index, float angleIncrement)
{
float ang = index * angleIncrement;
Vector3 pos;
pos.x = center.x + radius * Mathf.Sin(ang * Mathf.Deg2Rad);
pos.z = center.z + radius * Mathf.Cos(ang * Mathf.Deg2Rad);
pos.y = center.y;
return pos;
}
To call it, you'd do:
Vector3 center = transform.position;
float radius = (float)space / 2;
float angleIncrement = 360 / (float)numObjects;
for (int i = 0; i < numObjects; i++)
{
Vector3 pos = FormationCircle(center, radius, i, angleIncrement);
// ...
}
Hope this helps! Let me know if you have any questions.

Implementing water effects (splashes) into XNA 4.0 game

Edit: Link to Question on GameDev SE: https://gamedev.stackexchange.com/questions/51656/implementing-water-effects-splashes-into-xna-4-0-game
I am creating a 2D XNA game and came across a tutorial on adding water effects (splashes) to an XNA game but after implementing it into my game I can't get it to scale down. Currently it takes up the entire screen.
The Water class looks like this
class Water
{
struct WaterColumn
{
public float TargetHeight;
public float Height;
public float Speed;
public void Update(float dampening, float tension)
{
float x = TargetHeight - Height;
Speed += tension * x - Speed * dampening;
Height += Speed;
}
}
PrimitiveBatch pb;
WaterColumn[] columns = new WaterColumn[201];
static Random rand = new Random();
public float Tension = 0.025f;
public float Dampening = 0.025f;
public float Spread = 0.25f;
RenderTarget2D metaballTarget, particlesTarget;
SpriteBatch spriteBatch;
AlphaTestEffect alphaTest;
Texture2D particleTexture;
private float Scale { get { return spriteBatch.GraphicsDevice.Viewport.Width / (columns.Length - 1f); } }
List<Particle> particles = new List<Particle>();
class Particle
{
public Vector2 Position;
public Vector2 Velocity;
public float Orientation;
public Particle(Vector2 position, Vector2 velocity, float orientation)
{
Position = position;
Velocity = velocity;
Orientation = orientation;
}
}
public Water(GraphicsDevice device, Texture2D particleTexture)
{
pb = new PrimitiveBatch(device);
this.particleTexture = particleTexture;
spriteBatch = new SpriteBatch(device);
metaballTarget = new RenderTarget2D(device, device.Viewport.Width, device.Viewport.Height);
particlesTarget = new RenderTarget2D(device, device.Viewport.Width, device.Viewport.Height);
alphaTest = new AlphaTestEffect(device);
alphaTest.ReferenceAlpha = 175;
var view = device.Viewport;
alphaTest.Projection = Matrix.CreateTranslation(-0.5f, -0.5f, 0) *
Matrix.CreateOrthographicOffCenter(0, view.Width, view.Height, 0, 0, 1);
for (int i = 0; i < columns.Length; i++)
{
columns[i] = new WaterColumn()
{
Height = 240,
TargetHeight = 240,
Speed = 0
};
}
}
// Returns the height of the water at a given x coordinate.
public float GetHeight(float x)
{
if (x < 0 || x > 800)
return 240;
return columns[(int)(x / Scale)].Height;
}
void UpdateParticle(Particle particle)
{
const float Gravity = 0.3f;
particle.Velocity.Y += Gravity;
particle.Position += particle.Velocity;
particle.Orientation = GetAngle(particle.Velocity);
}
public void Splash(float xPosition, float speed)
{
int index = (int)MathHelper.Clamp(xPosition / Scale, 0, columns.Length - 1);
for (int i = Math.Max(0, index - 0); i < Math.Min(columns.Length - 1, index + 1); i++)
columns[index].Speed = speed;
CreateSplashParticles(xPosition, speed);
}
private void CreateSplashParticles(float xPosition, float speed)
{
float y = GetHeight(xPosition);
if (speed > 120)
{
for (int i = 0; i < speed / 8; i++)
{
Vector2 pos = new Vector2(xPosition, y) + GetRandomVector2(40);
Vector2 vel = FromPolar(MathHelper.ToRadians(GetRandomFloat(-150, -30)), GetRandomFloat(0, 0.5f * (float)Math.Sqrt(speed)));
CreateParticle(pos, vel);
}
}
}
private void CreateParticle(Vector2 pos, Vector2 velocity)
{
particles.Add(new Particle(pos, velocity, 0));
}
private Vector2 FromPolar(float angle, float magnitude)
{
return magnitude * new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
}
private float GetRandomFloat(float min, float max)
{
return (float)rand.NextDouble() * (max - min) + min;
}
private Vector2 GetRandomVector2(float maxLength)
{
return FromPolar(GetRandomFloat(-MathHelper.Pi, MathHelper.Pi), GetRandomFloat(0, maxLength));
}
private float GetAngle(Vector2 vector)
{
return (float)Math.Atan2(vector.Y, vector.X);
}
public void Update()
{
for (int i = 0; i < columns.Length; i++)
columns[i].Update(Dampening, Tension);
float[] lDeltas = new float[columns.Length];
float[] rDeltas = new float[columns.Length];
// do some passes where columns pull on their neighbours
for (int j = 0; j < 8; j++)
{
for (int i = 0; i < columns.Length; i++)
{
if (i > 0)
{
lDeltas[i] = Spread * (columns[i].Height - columns[i - 1].Height);
columns[i - 1].Speed += lDeltas[i];
}
if (i < columns.Length - 1)
{
rDeltas[i] = Spread * (columns[i].Height - columns[i + 1].Height);
columns[i + 1].Speed += rDeltas[i];
}
}
for (int i = 0; i < columns.Length; i++)
{
if (i > 0)
columns[i - 1].Height += lDeltas[i];
if (i < columns.Length - 1)
columns[i + 1].Height += rDeltas[i];
}
}
foreach (var particle in particles)
UpdateParticle(particle);
particles = particles.Where(x => x.Position.X >= 0 && x.Position.X <= 800 && x.Position.Y - 5 <= GetHeight(x.Position.X)).ToList();
}
public void DrawToRenderTargets()
{
GraphicsDevice device = spriteBatch.GraphicsDevice;
device.SetRenderTarget(metaballTarget);
device.Clear(Color.Transparent);
// draw particles to the metaball render target
spriteBatch.Begin(0, BlendState.Additive);
foreach (var particle in particles)
{
Vector2 origin = new Vector2(particleTexture.Width, particleTexture.Height) / 2f;
spriteBatch.Draw(particleTexture, particle.Position, null, Color.White, particle.Orientation, origin, 2f, 0, 0);
}
spriteBatch.End();
// draw a gradient above the water so the metaballs will fuse with the water's surface.
pb.Begin(PrimitiveType.TriangleList);
const float thickness = 20;
float scale = Scale;
for (int i = 1; i < columns.Length; i++)
{
Vector2 p1 = new Vector2((i - 1) * scale, columns[i - 1].Height);
Vector2 p2 = new Vector2(i * scale, columns[i].Height);
Vector2 p3 = new Vector2(p1.X, p1.Y - thickness);
Vector2 p4 = new Vector2(p2.X, p2.Y - thickness);
pb.AddVertex(p2, Color.White);
pb.AddVertex(p1, Color.White);
pb.AddVertex(p3, Color.Transparent);
pb.AddVertex(p3, Color.Transparent);
pb.AddVertex(p4, Color.Transparent);
pb.AddVertex(p2, Color.White);
}
pb.End();
// save the results in another render target (in particlesTarget)
device.SetRenderTarget(particlesTarget);
device.Clear(Color.Transparent);
spriteBatch.Begin(0, null, null, null, null, alphaTest);
spriteBatch.Draw(metaballTarget, Vector2.Zero, Color.White);
spriteBatch.End();
// switch back to drawing to the backbuffer.
device.SetRenderTarget(null);
}
public void Draw()
{
Color lightBlue = new Color(0.2f, 0.5f, 1f);
// draw the particles 3 times to create a bevelling effect
spriteBatch.Begin();
spriteBatch.Draw(particlesTarget, -Vector2.One, new Color(0.8f, 0.8f, 1f));
spriteBatch.Draw(particlesTarget, Vector2.One, new Color(0f, 0f, 0.2f));
spriteBatch.Draw(particlesTarget, Vector2.Zero, lightBlue);
spriteBatch.End();
// draw the waves
pb.Begin(PrimitiveType.TriangleList);
Color midnightBlue = new Color(0, 15, 40) * 0.9f;
lightBlue *= 0.8f;
float bottom = spriteBatch.GraphicsDevice.Viewport.Height;
float scale = Scale;
for (int i = 1; i < columns.Length; i++)
{
Vector2 p1 = new Vector2((i - 1) * scale, columns[i - 1].Height);
Vector2 p2 = new Vector2(i * scale, columns[i].Height);
Vector2 p3 = new Vector2(p2.X, bottom);
Vector2 p4 = new Vector2(p1.X, bottom);
pb.AddVertex(p1, lightBlue);
pb.AddVertex(p2, lightBlue);
pb.AddVertex(p3, midnightBlue);
pb.AddVertex(p1, lightBlue);
pb.AddVertex(p3, midnightBlue);
pb.AddVertex(p4, midnightBlue);
}
pb.End();
}
}
Then in the Game1.cs I have the following
LoadContent method
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>("Font");
particleImage = Content.Load<Texture2D>("metaparticle");
backgroundImage = Content.Load<Texture2D>("sky");
rockImage = Content.Load<Texture2D>("rock");
water = new Water(GraphicsDevice, particleImage);
.
.
.
}
In my update method I have the following (along with other code for the game, i am just showing water part)
protected override void Update(GameTime gameTime)
{
lastKeyState = keyState;
keyState = Keyboard.GetState();
lastMouseState = mouseState;
mouseState = Mouse.GetState();
water.Update();
Vector2 mousePos = new Vector2(mouseState.X, mouseState.Y);
// if the user clicked down, create a rock.
if (lastMouseState.LeftButton == ButtonState.Released && mouseState.LeftButton == ButtonState.Pressed)
{
rock = new Rock
{
Position = mousePos,
Velocity = (mousePos - new Vector2(lastMouseState.X, lastMouseState.Y)) / 5f
};
}
// update the rock if it exists
if (rock != null)
{
if (rock.Position.Y < 240 && rock.Position.Y + rock.Velocity.Y >= 240)
water.Splash(rock.Position.X, rock.Velocity.Y * rock.Velocity.Y * 5);
rock.Update(water);
if (rock.Position.Y > GraphicsDevice.Viewport.Height + rockImage.Height)
rock = null;
}
Then in the Draw method I have the following (when the active enum is InGame)
case ActiveScreen.InGame:
water.DrawToRenderTargets();
level.Draw(gameTime, spriteBatch);
DrawHud();
spriteBatch.Begin();
spriteBatch.Draw(backgroundImage, Vector2.Zero, Color.White);
if (rock != null)
rock.Draw(spriteBatch, rockImage);
spriteBatch.End();
water.Draw();
break;
My problem is this obviously takes up the entire screen. I realise why it takes up the entire screen but I can't figure out how to scale it down and set it on a fixed location in the game. If anyone could read through this and direct me towards how I would go about scaling this down successfully, I'd greatly appreciate it.
Notice that
private float Scale { get { return spriteBatch.GraphicsDevice.Viewport.Width / (columns.Length - 1f); } }
creates a Scale corresponding to the entire screen. And in both your Draw() and DrawToRenderTargets() methods
Vector2 p1 = new Vector2((i - 1) * scale, columns[i - 1].Height);
Vector2 p2 = new Vector2(i * scale, columns[i].Height);
which means that the top of the vectors (water columns) will start from the beginning of the screen. That's where you need to make change of.

Categories

Resources