How to make a GUI fade in roblox studio? - user-interface

Hello roblox studio scripters,
I'm a intermediate scripter-builder and need some help with the gui for my game.
I have a start screen with a play button like this:
I'm trying to fade out the gui when the button is clicked, but none of the tutorials worked. This is my script for the button:
local button = script.Parent
local gui = script.Parent.Parent.Parent
button.MouseButton1Down:Connect(function()
gui.Enabled = false
end)
I don't know how to do the changing, would it be BackgroundTransparency? How would you change the transparency from 0 to 1 in 0.01 increments?
I tried to make the gui fade with a for loop, changing the BackgroundTransparency but that didn't work, this is that code:
local button = script.Parent
local gui = script.Parent.Parent.Parent
button.MouseButton1Down:Connect(function()
for i = 0, 100, 1 do
gui.Frame.BackgroundTransparency + 0.01
wait(0.01)
gui.Enabled = false
end
end)
I don't know why it isn't working.
If I have a typo or something, please tell me.
Thanks!

The loop solution has a few typos, here it is fixed:
local button = script.Parent
local gui = script.Parent.Parent.Parent
button.MouseButton1Down:Connect(function()
for i = 0, 100 do
gui.Frame.BackgroundTransparency += 0.01 -- += adds 0.01 each time
task.wait(0.01) -- better than wait(0.01)
end
gui.Enabled = false
end)
However, this is not an ideal solution. A better system would use Roblox's TweenService to change the gui's transparency. Tweens are less jittery, are easier to modify, and have lots of customisation properties including repeating, changing length of time, and style of easing (e.g. going faster at first, then slower near the end; see Easing Styles on the Roblox docs).
local TweenService = game:GetService("TweenService")
local button = script.Parent
local gui = script.Parent.Parent.Parent
local tweenInfo = TweenInfo.new(
2, -- Time
Enum.EasingStyle.Linear, -- Easing Style
Enum.EasingDirection.Out -- Easing Direction
-- See https://create.roblox.com/docs/reference/engine/datatypes/TweenInfo for more available properties
)
local tween = TweenService:Create(
gui.Frame, -- Instance to tween
tweenInfo, -- TweenInfo
{ Transparency = 1 } -- What we want to change
)
button.MouseButton1Down:Connect(function()
tween:Play()
tween.Completed:Wait() -- Wait until tween is complete
gui.Enabled = false
end)
Though both of these solutions change only the transparency of the background, so the child elements, such as the Playbutton, will stay visible until the gui is disabled. You may wish to replace the Frame with a CanvasGroup, which also changes the transparency of its children when its GroupTransparency property is changed.
local tween = TweenService:Create(
gui.CanvasGroup, -- Instance to tween
tweenInfo, -- TweenInfo
{ GroupTransparency = 1 } -- What we want to change
)

Related

GODOT - Full Attack Animation Not Playing

I have this simple game i made in Godot Game Engine, and i have implemented some animations in my game's main character.
1. Run
This is a simple run animation i've added, which is played when the character is moving
2. Idle
This animation is more like a single image which is played when the character is not moving
3. Attack
This animation is played when user presses Left Mouse Button.
I am having my issue in attack animation, when i press Left Mouse Button my Animation doesn't play, instead i get first frame of the animation and then character goes back to idle Animation.
This is how it looks like:
This is my Character's Code:
extends KinematicBody2D
var _inputVec = Vector2.ZERO
var VELOCITY = Vector2.ZERO
var LAST_INPUT = Vector2.ZERO
const MAX_SPEED = 70
const ACCELERATION = 500
const FRICTION = 500
onready var animationPlayer = $AnimationPlayer
func _ready():
print("game started!")
func _physics_process(delta):
_inputVec.x = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
_inputVec.y = Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up")
_inputVec = _inputVec.normalized()
if _inputVec != Vector2.ZERO:
if _inputVec.x > 0:
animationPlayer.play("playerRunRight")
elif _inputVec.y < 0:
animationPlayer.play("playerRunUp")
elif _inputVec.y > 0:
animationPlayer.play("playerRunDown")
else:
animationPlayer.play("playerRunLeft")
VELOCITY = VELOCITY.move_toward(_inputVec * MAX_SPEED, ACCELERATION * delta)
LAST_INPUT = _inputVec
else:
VELOCITY = VELOCITY.move_toward(Vector2.ZERO, FRICTION * delta)
if Input.is_action_just_pressed("ui_lmb"):
if LAST_INPUT.x > 0:
animationPlayer.play("playerAttackRight")
elif LAST_INPUT.y < 0:
animationPlayer.play("playerAttackUp")
elif LAST_INPUT.y > 0:
animationPlayer.play("playerAttackDown")
else:
animationPlayer.play("playerAttackLeft")
else:
if LAST_INPUT.x > 0:
animationPlayer.play("playerIdleRight")
elif LAST_INPUT.y < 0:
animationPlayer.play("playerIdleUp")
elif LAST_INPUT.y > 0:
animationPlayer.play("playerIdleDown")
else:
animationPlayer.play("playerIdleLeft")
VELOCITY = move_and_slide(VELOCITY)
Full Project is Available at my Github Repo
Remember that _physics_process runs once per (physics) frame.
So, one frame you pressed the left mouse button, and this line got to execute:
animationPlayer.play("playerAttackRight")
But next (physics) frame, you had not just pressed the left mouse button, so this conditional is false:
if Input.is_action_just_pressed("ui_lmb"):
And then this line get to execute:
animationPlayer.play("playerIdleRight")
As a result, you only see about one frame of the "playerAttackRight" animation.
You are going to need to keep track of the current state (running, attacking, idle). The rule is that you can change from running to idle immediately, but you can only change from attacking to idle, when the attack animation ends.
You can keep track of the current state with a variable, of course. You can take input and the value of the state of the variable to decide the new state. Then separately read the state variable and decide which animation to play. You may also want to set the state variable when some animations end.
And to do something when an animation ends, you can either resource to yield:
yield(animationPlayer, "animation_finished")
Which will have your code resume after it receives the "animation_finished" signal.
Or, otherwise you can connect to the "animation_finished" signal.
By the way, you can also queue animations:
animationPlayer.queue("name_of_some_animation")
While using AnimationPlayer like you do is OK. When it gets complex, there is another tool you should consider: AnimationTree.
Create an AnimationTree node, give it your animation player, and set the root to a new AnimationNodeStateMachine. There you can create your state machine, and configure if the transition between them is immediate or at the end.
Then, from code, you can get the state machine playback object, like this:
var state_machine = $AnimationTree.get("parameters/playback")
You can ask it what the current state is with:
var state:String = state_machine.get_current_node()
Which you can use as part of the decision of which state you want to go to. And then tell it you want it to go to a different state, like this:
state_machine.travel("name_of_some_state")
And using that it will respect the transitions you set, so you do not have to worry about it in code.
You can find more information about using AnimationTree at:
Using AnimationTree
Controlling Animation States

How do I make a script affect all its children in Roblox LUA?

I'm new to programming in LUA, although I've learned similar languages like JS. It's frustrating if I have to alter the same script in many parts in a group by replacing each script, and I don't know of an elegant way to do it. Instead, I decided to nest all the parts inside of the script. I've seen some examples and I've tried to adapt some of them, but they don't exactly apply to what I want to do and I can't get them to work.
In essence, what I'm trying to do is monitor all the bricks for a player to contact them. I took the original disappearing brick script that was nested inside each brick and modified it. If a part (brick) is touched, that should call the onTouch function, which will make the brick's transparency decrease over time until the in pairs loop is done, after which the brick disappears and CanCollide is turned off. After 2 seconds, it then returns back to normal. I think the problem is with the coding I used to monitor the parts as I don't really understand the right way to monitor multiple objects. Can someone please help? Thanks!
File structure:
function onTouched(brick)
local delay = .1 -- the delay between each increase in transparency (affects speed of disappearance)
local RestoreDelay = 2 -- delay before the brick reappears
local inc = .1 -- how much the brick disappears each time
-- All characters have a Humanoid object
-- if the model has one, it is a character
local h = script.Child:findFirstChild("Humanoid") -- Find Humanoids in whatever touched this
if (h ~=nil) then -- If there is a Humanoid then
h.Health = h.MaxHealth -- Set the health to maximum (full healing)
for x=0,1, inc do
script.Child.Transparency = x+inc
script.Child.CanCollide = true
wait(delay)
end
wait(delay)
script.Child.Transparency = 1
script.Child.CanCollide = false
wait(RestoreDelay)
script.Child.Transparency = 0
script.Child.CanCollide = true
else
end
end
while true do
local bricks=script:GetChildren():IsA("basic.part")
for x=1,brick in pairs(bricks) do
brick.Touched:connect(onTouched(brick)) -- Make it call onTouched when touched
end
end
end
For the most part, you've gotten it right, but you've got a few syntax errors where there are different conventions between JavaScript and Lua.
In JS, you would fetch an array of objects and then bee able to filter it immediately, but in Lua, there is limited support for that. So a JavaScript line like :
var bricks = script.GetChildren().filter(function(item) {
return item === "basic.part"
})
cannot be done all in one line in Lua without assistance from some library. So you'll need to move the check into the loop as you iterate over the objects.
Other than that, the only other thing to change is the onTouched handler's function signature. The BasePart.Touched event tells you which object has touched the brick, not the brick itself. But by creating a higher order function, it's easy to get access to the brick, and the thing that touched it.
-- create a helper function to access the brick and the thing that touched it
function createOnTouched(brick)
-- keep track whether the animation is running
local isFading = false
return function(otherPart)
-- do not do the animation again if it has already started
if isFading then
return
end
local delay = .1 -- the delay between each increase in transparency (affects speed of disappearance)
local restoreDelay = 2 -- delay before the brick reappears
local inc = .1 -- how much the brick disappears each time
-- All characters have a Humanoid object, check for one
local h = otherPart.Parent:FindFirstChild("Humanoid")
if h then
-- heal the player
h.Health = h.MaxHealth
-- start fading the brick
isFading = true
brick.CanCollide = true
for i = 0, 1, inc do
brick.Transparency = i
wait(delay)
end
-- turn off collision for the brick
wait(delay)
brick.Transparency = 1
brick.Anchored = true
brick.CanCollide = false
-- turn the part back on
wait(restoreDelay)
brick.Transparency = 0
brick.CanCollide = true
-- reset the animation flag
isFading = false
end
end
end
-- loop over the children and connect touch events
local bricks = script:GetChildren()
for i, brick in ipairs(bricks) do
if brick:IsA("BasePart") then
local onTouchedFunc = createOnTouched(brick)
brick.Touched:Connect(onTouchedFunc)
end
end

Remove a screenGUI when unequipping

I already made a question on cooldown a weapon and I made a screenGUI to show the player when they can shoot again implementing the same debounce code. The problem is I've got no clue on how to delete the screengui/textlabel from the screen. As every tool I'm planing on doing has its own GUI, if the screenGUI of one tool doesn't delete, it will overlap with the same tool's GUI/ other tools GUI.
I already tried hiding the text label as stated in this question like this
player.PlayerGui.ScreenGui.TextLabel.Visible = false but
1) It only makes it disappear first time its unequipped and
2) Im afraid that given it doesn't get deleted, but rather hidden, after some time, stacked hidden GUIs will somehow affect the games smoothness in some way.
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local tool = script.Parent
--Creaets a text label with the text Block Ready! on it when the player
local function onEquip()
print("screengui1")
local screenGui = Instance.new("ScreenGui")
screenGui.Parent = player.PlayerGui
local textLabel = Instance.new("TextLabel")
textLabel.Parent = screenGui
textLabel.BackgroundTransparency = 0.85
textLabel.Position = UDim2.new(0, 1100, 0, 550)
textLabel.Size = UDim2.new(0, 150, 0, 50)
textLabel.BackgroundColor3 = BrickColor.White().Color
textLabel.Text = "Block ready!"
end
local isGunOnCooldown = false
local cooldownTime = 3 --seconds
mouse.Button1Down:Connect(function()
-- debounce clicks so the text dont changes (its cooldown is the same as the gun cooldown the GUI is within)
if isGunOnCooldown then
return
end
-- change the text,
isGunOnCooldown = true
player.PlayerGui.ScreenGui.TextLabel.Text = "Reloading..."
-- start the cooldown and reset it along with the test
spawn(function()
wait(cooldownTime)
isGunOnCooldown = false
player.PlayerGui.ScreenGui.TextLabel.Text = "Block ready!"
end)
end)
local function onUnequip()
--code to delete the gui goes here
end
tool.Equipped:connect(onEquip)
tool.Unequipped:connect(onUnequip)
I just need explanation on how to delete the screenGUI that contains the textlabel shown to the player when they unequip the weapon
The easiest way to handle this is to keep a reference to the UIElement when you first create it. Then when you equip the tool, you simply set the Parent. When you unequip, you set the Parent to nil. This way, you know that there will always be one element, you are simply controlling when it is visible.
local function createAmmoUI()
--Creates a text label with the text Block Ready! on it when the player
local screenGui = Instance.new("ScreenGui")
local textLabel = Instance.new("TextLabel")
textLabel.Name = "Message"
textLabel.Parent = screenGui
textLabel.BackgroundTransparency = 0.85
textLabel.Position = UDim2.new(0, 1100, 0, 550)
textLabel.Size = UDim2.new(0, 150, 0, 50)
textLabel.BackgroundColor3 = BrickColor.White().Color
textLabel.Text = "Block ready!"
return screenGui
end
-- make a persistent UI element to show off how much ammo is left and when reload is done
local ammoUI = createAmmoUI()
local function onEquip()
-- when the tool is equipped, also show the UI
print("Equipping Tool to current Player")
ammoUI.Parent = player.PlayerGui
end
local function onUnequip()
-- when the tool is unequipped, also remove the UI from the screen entirely
print("Unequiping Tool UI")
ammoUI.Parent = nil
end
local isGunOnCooldown = false
local cooldownTime = 3 --seconds
mouse.Button1Down:Connect(function()
-- debounce clicks so the text dont changes (its cooldown is the same as the gun cooldown the GUI is within)
if isGunOnCooldown then
return
end
-- change the text,
isGunOnCooldown = true
ammoUI.Message.Text = "Reloading..."
-- start the cooldown and reset it along with the test
spawn(function()
wait(cooldownTime)
isGunOnCooldown = false
ammoUI.Message.Text = "Block ready!"
end)
end)
tool.Equipped:connect(onEquip)
tool.Unequipped:connect(onUnequip)

GDScript: How to play an animation while key is preessed?

I am very new to coding and I'm still trying different languages out, I started off with GameMaker Studio and changed to Godot due to its compatibility with Mac I might as well learn something newer since GameMaker has been out for quite some time.
I want to create a RPG game and apply animation to each direction the character moves but the animation only plays after the key is pressed AND lifted. This means that while my key is pressed, the animation stops, and the animation only plays while my character is standing still, which is the complete opposite of what I want. The script looked really straight forward, but doesn't seem to be working.
I would tag this as the GDScript language instead of Python, but I guess I'm not reputable enough to make a new tag, so I tagged it under python because it is the most similar.
#variables
extends KinematicBody2D
const spd = 100
var direction = Vector2()
var anim_player = null
func _ready():
set_fixed_process(true)
anim_player = get_node("move/ani_move")
#movement and sprite change
func _fixed_process(delta):
if (Input.is_action_pressed("ui_left")) :
direction.x = -spd
anim_player.play("ani_player_left")
elif (Input.is_action_pressed("ui_right")):
direction.x = spd
anim_player.play("ani_player_right")
else:
direction.x = 0
if (Input.is_action_pressed("ui_up")) :
direction.y = -spd
anim_player.play("ani_player_up")
elif (Input.is_action_pressed("ui_down")):
direction.y = (spd)
anim_player.play("ani_player_down")
else:
direction.y = 0
if (Input.is_action_pressed("ui_right")) and (Input.is_action_pressed("ui_left")):
direction.x = 0
if (Input.is_action_pressed("ui_up")) and (Input.is_action_pressed("ui_down")) :
direction.y = 0
# move
var motion = direction * delta
move(motion)
As you check the input in _fixed_process, you call anim_player.play() several times a frame, which always seems to restart the animation, and thus, keeps the very first frame of the animation visible all the time.
As soon as you release the key, anim_player.play() stops resetting the animation back to start, and it can actually proceed to play the following frames.
A simple straight-forward solution would be to remember the last animation you played, and only call play() as soon as it changes.
You need to know if the animation has changed
First you need to put these variables in your code:
var currentAnim = ""
var newAnim = ""
And then you add this in your _fixed process:
if newAnim != anim:
anim = newAnim
anim_player.play(newAnim)
To change the animation you use:
newAnim = "new animation here"

How to make buttons stay pressed using corona

I am trying to get my buttons to stay "pressed" once it is released. Right now I am using the improved Buttons Module for corona and I have the default image being the button looking unpressed, and the over image being replaced by an image that looks pressed.
What I am trying to do is once the button is pressed, it stays on the over image. Here is how my code is set up for the button I am testing it on.
local digButton = buttons.newButton{
default = "digButton.png",
over = "digButtonPressed.png",
onEvent = digButtonFunction,
id = "dig"
}
digButton:setReferencePoint(display.CenterReferencePoint)
digButton.x = display.contentWidth/5
digButton.y = display.contentHeight/1.9
Also, I have a function (digButtonFunction) that sets the id of this button to a variable to be used to run an if statement for when the user pushes a button following this one.
This sounds to me like what you really want is a switch. Buttons are not really designed from a UI perspective to do that. The down-state is there just to give feedback to the user that some action happened.
If it were me, I'd not use the button bit at all, but load in to images using display.newImageRect() and draw the downstate first, then the upstate. Built a touch event listener on each one that will hide one or the other. I do this in my games for my sound on/off buttons.
local soundOn = true
local soundOnBtn, soundOffBtn
local function soundToggle(event)
if soundOn then
soundOn = false
soundOnBtn.isVisible = false
soundOffBtn.isVisible = true
else
soundOn = true
soundOnBtn.isVisible = true
soundOffBtn.isVisible = false
end
return true
end
soundOnBtn = display.newImageRect("images/switch_on.png", 46, 36)
soundOnBtn.x = display.contentWidth / 2 + 25
soundOnBtn.y = display.contentHeight / 2 - 15
group:insert(soundOnBtn)
soundOnBtn:addEventListener("tap", soundToggle)
soundOffBtn = display.newImageRect("images/switch_off.png", 46, 36)
soundOffBtn.x = display.contentWidth / 2 + 25
soundOffBtn.y = display.contentHeight / 2 - 15
group:insert(soundOffBtn)
soundOffBtn:addEventListener("tap", soundToggle)
soundOffBtn.isVisible = false

Resources