I've been working on a Breakout-style project recently and wanted to make it so that once a user enters the 'cheat mode', they'll be able to move the ball in their desired direction.
Here's what my code looks like: https://gist.github.com/anonymous/7589f4f141888c3c32c7
I've included the return; in my ball class due to the fact that if I don't, the program will not recognise the changes in the X and Y. So basically, my problem is, I want the program to ignore the existing x and y speeds and use the ones provided by the user if the cheat mode is enabled. (Meaning if the user enters the cheat mode, the program should stop the ball where it last was, in terms of x and y coordinates, and let the user move it)
Example of what I mean:
Player enters cheat mode -> Presses Up.
- Ball keeps moving up until it hits the top of the form and then should bounce back in the opposite direction to which it hit the wall.
How do I implement this into my program? I used the 'if statement' for the boolean cheat mode and the return function to exit the moveBall procedure (which ignores the xSpeed and ySpeed but ultimately stops the ball from moving on it's own completely, so the player has to keep clicking Up to move the ball up, otherwise it stays in place.
EDIT:
Okay, I've fixed the issue where the ball wouldn't move, I just added a few more variables for the X and Y change of the ball.
Here's what my code looks like now:
public void moveBall()
{
if (Form1.cheatModeClicked == true)
{
ballRec.X += cheatX;
ballRec.Y -= cheatY;
}
else
{
ballRec.X += xSpeed;
ballRec.Y -= ySpeed;
}
}
However, the ball collision still doesn't want to work, and I don't know why?
It sounds like you need to add a method to the ball class to allow the ball's speed to be set externally. Then you can call that method when keys are pressed, depending on the cheat mode state. The cheat mode state should be stored outside the ball class anyway. The ball itself should only know about ball things and not what a non-ball concept such as cheat mode is.
Here is some pseudo code, I might have missed a few things in the description but hopefully it helps
class Form
{
bool _inCheatMode = false;
Ball ball;
void EnterCheatMode()
{
_inCheatMode = true;
ball.SetSpeed(0, 0); //stop the ball
}
void ExitCheatMode()
{
_inCheatMode = false;
}
void KeyDown(Keys key)
{
if (_inCheatMode)
{
if (key == Keys.Up)
ball.SetSpeed(0, -1);
}
}
}
class Ball
{
public void SetSpeed(x, y)
{
}
}
Related
So, I've been trying to learn Unity these past couple of weeks. As a starter project I decided to try and replicate the mobile game Pop the Lock. Right now, I'm having some problems with my keyboard inputs that I just can't figure out how to solve.
This is how my game screen looks: GameScreen. The red bar is the Player and the yellow circle is the Target. As the game progresses, the Player rotates around the ring towards the Target.
Basically, if the Player and the Target are touching AND the Player presses the Space Key at the exact same time, the Player is supposed to gain a point. The Target is also supposed to be destroyed, and a new Target is supposed to randomly be spawned in somewhere on the game screen. At the moment, this system works ... most of the time. About 80% of the time my code operates as it should, but around 20% of the time my code doesn't register when player presses the space key as the two collide. Here's my code:
public class Target: MonoBehaviour {
public GameObject target;
void Update () {
if (Input.GetKeyDown("space")) {
Debug.Log("SPACE PRESSED!!");
}
}
private void OnTriggerEnter2D (Collider2D collision) {
Debug.Log("Collision!");
}
private void OnTriggerStay2D(Collider2D other) {
// This is the part that sometimes isn't registering:
if (Input.GetKeyDown("space")) {
Debug.Log("HIT!!");
Score.score++;
// Code to spawn new Target on random place in the ring:
// Seems to be working as intended:
float distance = 2.034822f;
float x = Random.Range(-2f, 2f);
float y = Mathf.Pow(distance,2) - Mathf.Pow(x,2);
y = Mathf.Sqrt(y);
float[] options = {y, -y};
int randomIndex = Random.Range(0, 2);
y = options[randomIndex];
Vector3 vector = new Vector3(x, y, 0);
GameObject newTarget = Instantiate(target, vector, Quaternion.identity);
Destroy(gameObject);
}
}
}
As you can see I have Log statements that print something every time the player and the target are touching, every time the space key is pressed, and every time the space key is pressed while they are touching. This is how the console looks like when everything is working : Image One. This is what the console looks like when my code isn't working : Image Two.
So even when it isn't working, the collision and the key press are still registered at the exact same time. But for some reason the hit itself isn't registered (so the if condition isn't passed). Because of this I'm quite confident that it's not just input delay or me pressing the key at the wrong time. As I mentioned above, this only happens about 20% of the time, which makes it even more confusing to me. The Target has a trigger collider2D and it also has a dynamic RigidBody2D with gravity scale set to 0 (as I was told it should). Any help would be greatly appreciated.
(How my collider and rigidbody look: Image)
Something you can do is to set a flag to becomes true while you are pressing the key in the update loop, so the update loop will convert to something like:
private bool isSpacePressed = false;
update() {
isSpacePressed = false;
if (Input.GetKeyDown(KeyCode.Space)){
isSpacePressed = true;
}
}
so every loop the flag will be set to false except if you are pressing the space bar and the OnTriggerStay2D while become something like
OnTriggerStay2D () {
if(isSpacePressed){
.. do magic..
}
}
Look that I replace the Input.GetKeyDown('space') to Input.GetKeyDown(KeyCode.Space) I recommend using this one to avoid typing errors
I'm just starting out please excuse vast ignorance.
I'm writing a c# script in unity as part of the essentials training. I'm doing the 3d audio module and I thought I'd try and get a little bit fancier than the scope of this particular lesson which is supposed to be having an object fly through a window in a pre-built scene and make a 3d sound as it moves.
I wanted to make the movement of the object conditional upon a player moving close to it in 3d space. I figured out how to trigger the movement of an object in a script with an if statement that changes the transform parameters of the object the script is attached to when a 'distanceFromObject' variable is < 2. It works, however the script runs in the update section of the script which runs once every frame. This means that the object's transform parameters are changed every frame as expected but of course stops doing so when the distance between the object that's moving and the player exceeds 2.
I see the mistake I've made because if the object moves away when the player gets close then it will inevitably eventually move far enough away that the distanceFromObject variable will grow bigger than 2 whereupon it stops and just hovers in place. I don't know how to fix it though.
I need the script to check the distance between the object and the player every frame so that it will trigger the instance the player gets close enough, and when they get close enough, I need the object to move away, however once it has been triggered to move, I need the object to continue moving, but the script to stop checking what the distance is anymore.
The script looks like this
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FlyOff : MonoBehaviour
{
public Vector3 rotateChange;
public Vector3 positionChange;
public float distanceFromObject;
public GameObject character;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
distanceFromObject = Vector3.Distance(character.transform.position, this.gameObject.transform.position);
print (distanceFromObject);
if (distanceFromObject < 2)
{
transform.Rotate (rotateChange);
transform.position += positionChange;
}
}
}
Use flags instead of writing your logic in the if statement :
public class FlyOff : MonoBehaviour
{
// fields removed for more readability
// use a flag that's set to true/false
private bool isCloseEnough = false;
void Update()
{
distanceFromObject = Vector3.Distance(character.transform.position, this.gameObject.transform.position);
print (distanceFromObject);
// set the flag to true when player is close enough
if (distanceFromObject < 2)
{
isCloseEnough = true;
}
// even if the player gets far, the flag will remain true
if (isCloseEnough)
{
transform.Rotate (rotateChange);
transform.position += positionChange;
}
}
}
You can even apply the opposite logic to stop the object to move away when it has reach a certain distance :
if (distanceFromObject < 2)
{
isCloseEnough = true;
}
else if (distanceFromObject > SomeValue)
{
isCloseEnough = false;
}
If I understand correctly you could just add a bool flag and set it once you are close enough. Then you can start moving and skip further distance checks but keep moving forever.
private bool flyAway;
void Update()
{
if(!flyAway)
{
distanceFromObject = Vector3.Distance(character.transform.position, transform.position);
print (distanceFromObject);
if (distanceFromObject < 2)
{
flyAway = true;
}
}
else
{
transform.Rotate (rotateChange);
transform.position += positionChange;
}
}
In general: Avoid using print every frame! Even if you user doesn't see the log in a built app it is still causing overhead!
I am creating a video game where the character has to travel within literal art canvases (the ones that you use for painting) to reach the end goal.
Note that the "canvas" I am referring is not the UI element, but the actual canvases you would see in real life.
I based my code off the concepts of portals. This may not be the most efficient way of dealing with it and I will consider all advices.
This is my current code:
public Transform PortalB;
public CharacterController2D Player;
public GameObject PlayerObject;
private GameObject CloneTemporary;
public Queue<GameObject> Clones = new Queue<GameObject>();
private bool isCreated = false;
// Use this for initialization
void Start () {
Clones.Enqueue(PlayerObject);
}
// Update is called once per frame
void Update () {
// Portal adapts to the player's current position
if (Player == null) {
Player = GameObject.FindWithTag("Player").GetComponent<CharacterController2D>();
PlayerObject = GameObject.FindWithTag("Player");
}
}
void OnTriggerEnter2D (Collider2D other) {
if (other.gameObject.tag == "Player") {
if (Vector2.Distance(transform.position, other.transform.position) > 0.7f) {
if (!isCreated) {
CloneTemporary = Instantiate(PlayerObject, new Vector2 (PortalB.position.x, PortalB.position.y), Quaternion.identity) as GameObject;
Clones.Enqueue(CloneTemporary);
isCreated = true;
}
}
}
}
void OnTriggerExit2D (Collider2D other) {
if (Vector2.Distance(transform.position, other.transform.position) > 0.7f) {
print(Clones.Count);
if (Clones.Count > 1) {
UnityEngine.Object.Destroy(Clones.Dequeue());
}
}
isCreated = false;
}
The "original character" will collide with the portal's box collider and create a copy on the other end of the portal. The box collider is annotated in red in the first image below. Note that the sizes are exactly the same.
Note that it technically is not a portal, but since it's a box which brings a character from one place to another, I might as well call it a portal.
Once the original leaves the box collider, it will get deleted, and the clone will then become the "original".
I am using a queue system to determine which "clone" gets deleted first.
There are a few problems with this:
It is very inefficient. I have to create portals manually for every intersection point in the canvases. Imagine a level full of canvases, and imagine a game with full of levels...
When it touches the portal, a duplicate will get spawned by the original character. There are three characters, but the Clones.Count only registers two.
I am not sure how well the code will work for vertical traverses.
When the character crosses the portal, it should be able to turn back. In this code, the character would glitch through the floor if he were to turn back and not get deleted by the OnExit function. I suspect this has something to do with the size of the portal, but I can foresee that even if it were to be bigger, the character would immediately disappear if he turns back.
I think the OnTriggerEnter function gets activated when the character is teleported on the other side. This might have an effect on the errors I just stated, but it may cause more in the near future.
I'm having trouble figuring out how to make the computer's paddle move. I have set up the player's paddle using a mouse. This is what I have to move the paddle using mouse. How do I make the computer's paddle move where the ball is going (with chances that the computer will lose)? Cheers!
Form.Cs
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
controller.MoveLeftPaddle(e.Location.Y);
}
Controller.Cs
public void MoveLeftPaddle(int newPlayerPosition)
{
paddlePlayer.MovePlayerPaddle(newPlayerPosition);
}
Paddle.CS
public void MovePlayerPaddle(int newYPosition)
{
position.Y = newYPosition;
}
Now, I have this code that I tried to make the computer's paddle move.
Paddle.CS
public void MoveComputerPaddle(int newY2Position)
{
position.Y = newY2Position;
}
Controller.Cs
public void MoveRightPaddle(int newComputerPosition)
{
if (ball.Position.Y >= paddleComputer.Position.Y)
{
newComputerPosition += BALLSPEED;
}
else if (ball.Position.Y <= paddleComputer.Position.Y)
{
newComputerPosition -= ball.Velocity.Y;
}
}
public void Run()
{
MoveRightPaddle(paddleComputer.Position.Y);
} //im not really sure about this part. this is the only way that I didnt get squiggly line.
then I have a method in Controller.cs to make the ball move,bounce and draw. I use it as well to draw the paddle which is inside the method Run(). Code above
Ideally your paddles for the Computer and the Player would be the same, and you would have a separate controller for each - The PlayerController would take user input and call MovePaddle based on that, wheras the ComputerController would check the ball position to decide how to call MovePaddle.
You could make the ComputerController store the position for the previous X frames, allowing you to tweak difficulty simply by choosing how old the data is that it uses for its decisions.
As 500 - Internal Server Error has pointed out in the comment section, you are only changing the passed in parameter's value (in this case, newComputerPosition), not paddleComputer.Position.Y.
I don't think you need any parameters at all for MoveRightPaddle like so:
public void MoveRightPaddle()
{
if (ball.Position.Y >= paddleComputer.Position.Y)
{
paddleComputer.Position.Y += BALLSPEED; // I don't know why this uses a different value from the else statement
}
else
{
paddleComputer.Position.Y -= ball.Velocity.Y;
}
}
and
public void Run()
{
MoveRightPaddle();
}
However, I am sensing there is a code smell somewhere if you are calling these variables directly, and thus I would suggest looking into ways of addressing this issue.
How to make the right paddle better at moving
Currently, your right paddle will continue follow the ball regardless of whether the ball is heading towards it or not, or if the paddle will go out of bounds or not.
If you know the x direction of the ball, you can quickly assert whether the right paddle even needs to move or not.
For instance:
if (ball_x_direction > 0) // because for the ball to go right, the direction needs to be positive
{
// move paddle here:
}
For checking if the paddle is going to go off screen (only showing for top of screen (i.e. the second condition of MoveRightPaddle)):
if (paddleComputer.Position.Y - ball.Velocity.Y < 0)
paddleComputer.Position.Y = 0;
} else {
paddleComputer.Position.Y -= ball.Velocity.Y;
}
I am having a big struggle implementing a collision detect on a list of bricks.
I have a game which randomly drops bricks that are supposed to stack up at the bottom of the screen. I managed to make them stop at the bottom of the screen but they don't stack.
I have this in my update function:
if (r.Next(0, 50) == 8)
{
_bricks.Add(new NormalBrick(this, new Vector2(r.Next(0, 700), 100)));
}
foreach(Brick b in _bricks)
{
b.move(GraphicsDevice.Viewport);
}
My move() function has the following code:
public void move(Viewport viewport)
{
if (_position.Y == (viewport.Height - _texture.Height ))
{
_position = new Vector2(_position.X, _position.Y);
}
else
{
_position = new Vector2(_position.X, _position.Y + _speed);
}
}
How can I make sure that the bricks don't all stop at the bottom of the screen, instead the brick has to check if there is a brick beneath it?
I have checked other questions on here but I couldn't find my answer and I have tried several things to get it fixed. Any help would be appreciated.
I would create Rectangles for each of your Bricks (unless you have already done so). Then in the Update() method, use something like brick.Rectangle.Intersects(anotherBrick) after movement. If true, then position the current brick above the intersecting bottom brick.
Make sure to move the brick's rectangle each time the brick moves.
I hope this helps. Let me know if you require any further assistance.