Currently I try to write a 2D roulette wheel in C# using Monogame as framework.
I'm quiet experienced with Monogame but I never had to manage fast moving textures to stick together tho.
The idea is that 3 textures(black, red, green) are connected to each other in a line of 15 objects.
That's fine as long as I don't start to scroll all these objects to actually "make the wheel move".
After moving for a while, the textures are no more connected to each other. They get some space in between or start to overlap.
I made a short Video to clarify the problem: https://puu.sh/vwbyU/d396c6ad99.mp4 (It's about 10 MB, some Browsers may have download it before it shows up)
This is the most important code:
const int _roulleteOffset = 170; // X coordinate to start at
Sprite[] fieldList = new Sprite[15]; // Represents my wheel (Array of my roulette fields/textures)
Rectangle scrollArea; // Fixed Area for the complete fieldList
float scrollSpeed = 0.0f; // Scrollspeed of the wheel. 0 on start.
// First I call the Content.Load to fill fieldList.Texture then
// this is called to position the "wheel" objects
private void AdditionalInit()
{
for (int i = 0; i < fieldList.Length; i++)
{
fieldList[i].Position = new Vector2(_roulleteOffset + i * fieldList[i].Texture.Width, Statics.GAME_WINDOW_HEIGHT / 2 - fieldList[i].Texture.Height / 2);
}
scrollArea = new Rectangle((int)fieldList[0].Position.X, (int)fieldList[0].Position.Y, fieldList[0].Texture.Height * fieldList.Length + 30, fieldList[0].Texture.Height);
}
// This Method is called in Update() - And I guess the problem has to be fixed here
private void ScrollRoulette()
{
for (int i = 0; i < fieldList.Length; i++)
{
fieldList[i].position.X += scrollSpeed; // scrollSpeed is set with pressing + or - on keyboard
if (fieldList[i].Position.X >= scrollArea.Width + fieldList[i].Texture.Width)
{
// After leaving the "scrollArea" set it to the start of it (Leaving on right side and entering at the left side again)
fieldList[i].position.X = scrollArea.X - fieldList[i].Texture.Width;
}
}
}
// The part of my Draw Method. But I don't think that I made a mistake here
public override void Draw(GameTime gameTime)
{
Statics.SPRITEBATCH.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend);
for (int i = 0; i < fieldList.Length; i++)
{
Statics.SPRITEBATCH.Draw(fieldList[i].Texture, fieldList[i].Position, null, Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0f);
}
Statics.SPRITEBATCH.End();
}
Just watch the Video to understand what I'm talking about. Maybe you can help me with it. Even on slow speed the textures starts to disconnect or overlap over time.
With help from another german forum I resolved the problem! Thanks god. Took me a whole day these 1 line. I just had to adjust the positioning of the incoming tile. Not just set pos.x to the start.
private void ScrollRoulette()
{
for (int i = 0; i < fieldList.Length; i++)
{
fieldList[i].position.X += scrollSpeed;
if (fieldList[i].Position.X >= scrollArea.Width + (fieldList[i].Texture.Width * 2))
{
// This works now
fieldList[i].position.X = scrollArea.X + (fieldList[i].Position.X - (scrollArea.Width + scrollArea.X) );
}
}
}
Related
In few last days trying to implement TinyKeep's dungeon generation algorithm myself.
Link to TinyKeep's dungeon generation algorithm
I don't understand it perfectly by 100%, because of it here am I :)
Firstly I generated set of rooms in circle
Going for each room in set. Pick it and going with another for loop again in set to compare every room intersection.
If room intersects, I added force, in which I will be apply separation. After that nested loop ended, I move current room, from first loop, according to that force vector. And so on. It happening until every room is not overlapped with others.
Also I picked center(Middle) point of rects ( x+width/2f, y+height/2f) to calculate that force vector
If vector is normalized, then rooms tighly packed, but if no - rooms have much space between them...
I wanna just recreate and understand TinyKeep's algorithm, but results not the same...
Also I getting weird results, then tall/wide rooms or always going in corners, tall go upper-left corner of map, wide - bottom-right corner. And all sometimes have overall diagonal separate direction
// Main method
private void Separate()
{
do
{
for (int i = 0; i < rooms.Count; i++)
{
Vector2 force = Vector2.zero;
int overlapCounter = 0;
for (int j = 0; j < rooms.Count; j++)
{
if (i == j) continue;
if (!rooms[i].IsOverlapped(rooms[j])) continue;
force += rooms[j].Middle - rooms[i].Middle;
overlapCounter++;
}
if (overlapCounter == 0) continue;
force /= overlapCounter;
force.Normalize();
force *= -1f;
rooms[i].Move(force);
}
} while (IsAnyRoomOverlapped());
StopTimer(true);
}
// Room.Move()
public void Move(Vector2 move)
{
x += Mathf.CeilToInt(move.x)* TILE_SIZE;
y += Mathf.CeilToInt(move.y)* TILE_SIZE;
}
// Constructor
public Dungeon(int roomAmount, int seed,float radius)
{
StartTimer();
this.roomAmount = roomAmount;
this.radius = radius;
Seed = seed;
InitializeRandom();
PopulateDungeonWithRooms();
}
While trying to figure out how solve this problem I read some questions and look their source code. I find that they not only move room after every overlapped neighbour found but also move neighbour in other direction!
What about directional behaviour of separation? It's my mistake or misunderstanding of rounding to integer
What about perfomance? Generating 512 rooms inside circle with radius of 128 units using normal distribution give 1100 ms
Now, code looks like:
private void SeparateRooms()
{
do
{
for (int current = 0; current < rooms.Count; current++)
{
for (int other = 0; other < rooms.Count; other++)
{
if (current == other || !rooms[current].IsOverlapping(rooms[other])) continue;
var direction = (rooms[other].Middle - rooms[current].Middle).normalized;
rooms[current].Move(-direction, TILE_SIZE);
rooms[other].Move(direction, TILE_SIZE);
}
}
}
while (IsAnyRoomOverlapped());
StopTimer(true);
}
And Room's Move() method:
public void Move(Vector2 move,int tileSize=1)
{
x += Mathf.RoundToInt(move.x)*tileSize;
y += Mathf.RoundToInt(move.y)*tileSize;
}
I am working on a top-down shooter game project. I have been having problems creating a triple shot effect where the player would shoot 3 bullets. These bullets would be parallel to each other at the same angle as where the player would aim.
The above example shows two fire angles from the player where one goes to the left and one goes up.
The current problem is that i have little to no idea how i would attempt to do this as i am quite bad at math. My first attempt went very poorly as seen below.
https://gyazo.com/c5be55ce07f7e81ad1786cd91ecc37ad
I have researched this in my previous math books and i have come to the conclusion that i need to be focusing on trigonometry (specifically sin and cos). I just dont know how exactly i am going to implement the logic for the triple shot effect.
Here is the code for the first attempt:
public class TripleShot : CoreWeaponPart
{
public enum FireType { Spread, Row }
public FireType fireType;
public override Bullet[] FirePattern(PlayerController owner, Weapon mainWeapon)
{
Bullet[] bullets = new Bullet[3 + mainWeapon.fireAmount];
for (int i = 0; i < bullets.Length; i++)
{
Bullet bullet = ObjectPooler.GetPooledObject<Bullet>(true); //Get an available bullet from the pool of bullets.
bullet.Damage = mainWeapon.damage;
bullet.Lifespan = mainWeapon.bulletLifespan;
bullet.Owner = owner;
switch (fireType)
{
case FireType.Spread: //Create a spread effect for the tripleshot.
bullet.transform.position = owner.barrelPos.position;
bullet.transform.rotation = (owner.barrelPos.rotation * Quaternion.Euler(0, -29.5f, 0)) * Quaternion.Euler(0, 15f * (i + 1f + mainWeapon.fireSpread), 0);
break;
case FireType.Row: //Create a parallel effect for the tripleshot (current problem).
bullet.transform.position = owner.barrelPos.position - (transform.forward + new Vector3(5 + mainWeapon.fireSpread, 0, 0)) + (new Vector3(5 * (i + 1f + mainWeapon.fireSpread), 0, 0));
bullet.transform.rotation = owner.barrelPos.rotation;
break;
}
bullet.Direction = bullet.transform.forward.normalized * mainWeapon.bulletVelocity;
bullet.SetActive(true);
bullets[i] = bullet;
}
return bullets;
}
}
You don't need to use trigonometry for this. Instead, use transforms to do the work for you.
First, add a maxParallelSpread term so you can prevent bullets from being fired from an absurd distance away when very many bullets are being fired.
Then, you can define parallelSpread, the distance for the bullets to go to the left or the right of the barrel to be Mathf.Min(maxParallelSpread, (bullets.Length-1) * mainWeapon.fireSpread). Then each bullet is positioned at this portion along that distance: (float)i/(bullets.Length-1)
You can use Mathf.Lerp(-parallelSpread, parallelSpread, (float)i/(bullets.Length-1)) to calculate how far to move the bullet along transform.right from the barrel's position.
Altogether:
public class TripleShot : CoreWeaponPart
{
public enum FireType { Spread, Row }
public FireType fireType;
public maxParallelSpread = 5f;
public override Bullet[] FirePattern(PlayerController owner, Weapon mainWeapon)
{
Bullet[] bullets = new Bullet[3 + mainWeapon.fireAmount];
float parallelSpread = Mathf.Min(maxParallelWidth, (bullets.Length-1)
* mainWeapon.fireSpread);
for (int i = 0; i < bullets.Length; i++)
{
//Get an available bullet from the pool of bullets.
Bullet bullet = ObjectPooler.GetPooledObject<Bullet>(true);
bullet.Damage = mainWeapon.damage;
bullet.Lifespan = mainWeapon.bulletLifespan;
bullet.Owner = owner;
switch (fireType)
{
case FireType.Spread: //Create a spread effect for the tripleshot.
bullet.transform.position = owner.barrelPos.position;
bullet.transform.rotation = ( owner.barrelPos.rotation
* Quaternion.Euler(0, -29.5f, 0))
* Quaternion.Euler(0, 15f * (i + 1f
+ mainWeapon.fireSpread), 0);
break;
case FireType.Row: //Create a parallel effect for the tripleshot
float bulletRightness = Mathf.Lerp(-parallelSpread,
parallelSpread, (float)i/(bullets.Length-1));
bullet.transform.position = owner.barrelPos.position
+ transform.right * bulletRightness;
bullet.transform.rotation = owner.barrelPos.rotation;
break;
}
bullet.Direction = bullet.transform.forward.normalized
* mainWeapon.bulletVelocity;
bullet.SetActive(true);
bullets[i] = bullet;
}
return bullets;
}
}
And you can adjust how much spreading mainWeapon.fireSpread results in parallelSpread by multiplying a constant that suits your needs. for instance, if you need it to spread twice as much, multiply it by 2f:
float parallelSpread = Mathf.Min(maxParallelWidth, (bullets.Length-1)
* 2f * mainWeapon.fireSpread);
My grid is on an array and I iterate through each grid tile to check to see if it is null and if the one above it isn't it will drop the block to the one below.
The code worked perfectly fine when I did this instantly, but once I added a coroutine so I drop the blocks slowly it stopped working. I'm fairly sure it's because the loop is checking blocks before they are set properly but I'm not sure how to go about fixing that problem.
private void UpdateBoard()
{
// Need to figure out how to adjust my grid objects when a word has been destroyed.
for (int x = 0; x < grid.width; x++)
{
for (int y = 0; y < grid.height - 1; y++)
{
if (grid.tiles[x, y] == null && grid.tiles[x, y + 1] != null)
{
StartCoroutine(BlockFall( x, y + 1 ));
// grid.tiles[x, y + 1].transform.position = new Vector2(grid.tiles[x, y + 1].transform.position.x, grid.tiles[x, y + 1].transform.position.y - 1);
}
}
}
}
public IEnumerator BlockFall(int posX, int posY)
{
float startY = 1;
grid.tiles[posX, posY].pos.y = grid.tiles[posX, posY].pos.y - 1;
grid.tiles[posX, posY - 1] = grid.tiles[posX, posY];
while(startY > 0)
{
startY -= 0.25f;
grid.tiles[posX, posY].transform.position = new Vector2(grid.tiles[posX, posY].transform.position.x, grid.tiles[posX, posY].transform.position.y - 0.25f);
yield return new WaitForSeconds(0.1f);
}
grid.tiles[posX, posY] = null;
}
Those are the two functions that are important. It's a bit messy right now perhaps, but it worked.
What happens now, is that the first block will fall, but the ones above it won't. That was working when instant though.
Try running the whole loop in the Coroutine or using "yield return new WaitForEndOfFrame();" at the beginning of the Coroutine. Maybe this way you will find whats the issue.
Coroutines are not sync with unity update functions in case you have code in Update(), FixedUpdates()... and you may face problems because of that.
All right, I fixed the issue and I guess I didn't give enough information to solve the problem the way I ended up doing it but I'll add what I did.
I have an activeLetter variable which contains the current block's information. I moved my UpdateBoard() into a FixedUpdate function and then set a check to make sure it ignored the activeLetter position.
It works perfect now.
I'm making bubble shooter replica in unity and I encountered a problem with initalizing the bubbles in random places, should I create an array of them in the background class?
For example i have class Sprite1 and Sprite2 and I want to display them in random places in the background how can I do it?
This is how it looks.
http://imgur.com/a/DGpZj
And below it's my attempt to display it by for loop, but dont know the method.
void Start () {
Random.Range(0f,4.5f);
for (int i = 0; i < 100; i++)
{
Sprite.Create(?)
}
}
Displaying your bubbles randomly in the background all depends on what your "randomly" word means: if it like randomly on a grid, completely randomly, can they overlay other bubbles, ...
So here's a first approach to your problem using a grid generation: to generate your bubbles, you can either use the Instantiate method or create new GameObjects.
Your game seems to be a 2D one so I highly recommend you looking for the Unity UI tutorials. Once you'll understand the basics of the UI system, create a Canvas and add an empty game object to it. Afterward you can assign it the below script:
using UnityEngine;
using UnityEngine.UI;
[RequireComponent(typeof(RectTransform))]
public class TestScript: MonoBehaviour
{
[SerializeField]
private float m_BubbleSize;
[SerializeField]
private int m_BubbleColumns;
[SerializeField]
private int m_BubbleRows;
[SerializeField]
private Sprite[] m_BubbleSprites;
private GameObject[][] m_Bubbles;
void Start()
{
GenerateBubbles();
}
private void GenerateBubbles()
{
// Position of the most top left bubble
Vector3 initialPosition = GetComponent<RectTransform>().position;
initialPosition.y += GetComponent<RectTransform>().rect.height * 0.5f;
initialPosition.y -= 0.5f * m_BubbleSize;
initialPosition.x -= (m_BubbleColumns - 1) * 0.5f * m_BubbleSize;
initialPosition.x -= 0.25f * m_BubbleSize;
// Rows height: comes from https://en.wikipedia.org/wiki/Close-packing_of_equal_spheres
float rowsHeight = Mathf.Sqrt(6.0f * m_BubbleSize * m_BubbleSize) / 3.0f;
// Bubbles references array
m_Bubbles = new GameObject[m_BubbleColumns][];
for(int i = 0; i < m_Bubbles.Length; i++)
{
m_Bubbles[i] = new GameObject[m_BubbleRows];
}
// Generation
for(int x = 0; x < m_Bubbles.Length; x++)
{
for(int y = 0; y < m_Bubbles[x].Length; y++)
{
GameObject bubble = new GameObject("Bubble_" + x.ToString() + y.ToString(), new System.Type[] { typeof(RectTransform), typeof(Image), typeof(CircleCollider2D) });
bubble.transform.SetParent(transform);
if(y % 2 == 0)
{
bubble.GetComponent<RectTransform>().position = initialPosition + new Vector3(x * m_BubbleSize, -y * rowsHeight, 0.0f);
}
else
{
bubble.GetComponent<RectTransform>().position = initialPosition + new Vector3(0.5f * m_BubbleSize + x * m_BubbleSize, -y * rowsHeight, 0.0f);
}
bubble.GetComponent<RectTransform>().sizeDelta = new Vector2(m_BubbleSize, m_BubbleSize);
bubble.GetComponent<Image>().sprite = m_BubbleSprites[Random.Range(0, m_BubbleSprites.Length)];
bubble.GetComponent<CircleCollider2D>().radius = m_BubbleSize * 0.5f;
}
}
}
}
To complete your game here are the few (maybe not exhaustive list) steps you will need to go through:
Populating a list of the potential "cell" where bubbles can be
Creating a pool of bubble to be fired (with random shuffling)
Writing the shooting + rebounds dynamic (Unity 2D Physic can help with that)
Detecting collision after bubble has been fired
Finding nearest possible "cell"
Searching if enough bubbles are connected (and if so destroying them)
Searching if some of the bubbles aren't connected anymore (and if so deleting them too)
OPTIONAL Implementing a scoring system (and saving the scores)
OPTIONAL Implementing apparition of new bubbles row on top every X bubbles fired
...
Hope this helps,
UPDATE
I managed to figure the answer out myself, what was happening was that my movement function was only registering the players original static position instead of the dynamic coordinates as the player moved around the screen. What I did was create a 'God' class to publicly store the players current X and Y coordinates, and then reference the 'god' class coordinates in my enemy's movement function.
Now, not only does the enemy detect when the player is within it's sights, it will follow the player changing directions accordingly, until it can't 'see' them anymore :D
If anyone wants to see the updated code just ask :)
I'm trying to make a program that contains player and enemy sprites, but I'm having a little trouble with the code for the 'Enemy'. Most of the coding problems on here deal with XNA, which I'm not using, so I was wondering if I could have a little help.
Basically, when the player moves within a certain distance, the movement function for the enemy should activate and it will move towards the players location as long as they're in the 'detection zone'. The enemy's sprite also has a walk animation for each direction, and it should flip through frames depending on whether it's currently moving or not.
The movement function is within a timer, so that it'll update with every tick. I have two separate functions for updating its location, one to physically change it's coordinates in the Form based on the data received from the movement function, and another to update the current frame from my sprite sheet.
The problem is that the enemy doesn't move whatsoever, if I get rid of it's detection and the code to follow the player, and instead just move it across the screen, it works perfectly fine. But unfortunately that's not what I'm trying to achieve.
The code works perfectly fine for my player sprite (albeit, it's controlled by the user and the keyboard keys), so I'm not sure what I'm doing wrong.
I believe the problems lies within the Movement function, but I'm not sure what that problem is exactly.
Here's my code for the relevant functions:
public void Movement() //This function is in a timer, and determines which direction the sprite will move and face
{
//The sprite rectangle is the position of the player sprite, and detection is the area where the enemy would 'see' the player if they entered it
sprite = new Rectangle(charX - 1, charY + 1, charWidth * 2 + 2, charHeight * 2 + 2);
Detection = new Rectangle(curX - 25, curY - 25, Enemy.Width * 6, Enemy.Height * 6);
if (Detection.IntersectsWith(sprite)) //Checks to see if the player is within range
{
//Moves the enemy based on the players position and changes direction accordingly
if (curX > charX)
{
curDirection = RIGHT;
dX = -1;
dY = 0;
numUpdates = 0;
curAnimation = MOVING;
}
else if (curX < charX)
{
curDirection = LEFT;
dX = 1;
dY = 0;
numUpdates = 0;
curAnimation = MOVING;
}
if (curY > charY)
{
curDirection = UP;
dY = -1;
dX = 0;
numUpdates = 0;
curAnimation = MOVING;
}
else if (curY < charY)
{
curDirection = DOWN;
dY = 1;
dX = 0;
numUpdates = 0;
curAnimation = MOVING;
}
}
//If not within range, don't move the sprite
else
{
dY = 0;
dX = 0;
curAnimation = FINISHED;
}
}
These are the updating functions that move the enemy and change the current frame
public void Follow() //Physcially moves the enemy across the form
{
int xMod = 0;
int yMod = 0;
if (dX != 0)
xMod = dX / Math.Abs(dX) * 2;
if (dY != 0)
yMod = dY / Math.Abs(dY) * 2;
//Moves the sprite across the x and y axis
curX += xMod;
dX += -xMod;
curY += yMod;
dY += -yMod;
}
public void UpdateFrame(int direction = DOWN)
{
numUpdates++;
switch (curAnimation)
{
//Stops the animation if the sprite stops moving
case FINISHED:
curFrame = 0;
break;
//Moves to the next frame when movement is found
case MOVING:
curFrame = (curFrame + 1) % 3;
Follow();
//check if done animation
if (dX == 0 && dY == 0) curAnimation = FINISHED;
break;
}
}
If you need more code just ask, any help is appreciated :)