-
Notifications
You must be signed in to change notification settings - Fork 445
Common theming issues, mistakes, tips, and troubleshooting
- If this is a new file, make sure you hit F2 to reload and check for new resources.
- Is the file in the correct place and named correctly?
- Is error reporting enabled? (F3+F6, make sure "show errors" is not greyed out)
- Make sure this file is not a duplicate of an existing screen, such as a ScreenTitleMenu overlay folder and then a lua file named ScreenTitleMenu overlay.lua.
You probably forgot to return the ActorFrame table in one of your files.
You might be returning a table instead of an Actor.
BGAnimations/ScreenSystemLayer overlay has been overwritten and is missing the messagecommand required to show messages.
If you've manually inserted it into the ActorFrame at a specific index, remember that lua is 1-indexed and actors at an index of 0 and below will not be compiled.
List of things that beginners do. Don't do this!! Every beginning themer should read this before it's too late and you have to spend hours fixing your theme!
Example 1 of what not to do:
t[#t+1] = Def.ActorFrame{
LoadActor("Sprite1")..{
InitCommand=cmd(xy,200,300);
};
LoadActor("Sprite2")..{
InitCommand=cmd(xy,200,350);
};
LoadActor("Sprite3")..{
InitCommand=cmd(xy,200,400);
};
LoadActor("Sprite4")..{
InitCommand=cmd(xy,200,450);
};
};
In this example, only the y value is changing, and it's only changing by 50 steps every time. Plus if you want to change the position to say, x=200 and y=500, you'd have to adjust the position of all four items. This defeats the purpose of using an ActorFrame in the first place.
The correct solution is to do your positioning inside the ActorFrame, then adjust the offset of the item within the item.
t[#t+1] = Def.ActorFrame{
--This positions the ActorFrame and all objects inside it at 200,300.
--If you ever need to move the position of all the objects inside this
--ActorFrame, you can change this command only.
InitCommand=cmd(xy,200,300);
LoadActor("Sprite1")..{
--This sprite is not being adjusted, so there is no command needed.
--InitCommand=cmd(y,0);
};
LoadActor("Sprite2")..{
--[[
This sprite is being adjusted by 50 pixels. It's position on the screen
is the position of the ActorFrame plus any xy commands.
The resulting position of this object is 350.
]]
InitCommand=cmd(y,50);
};
LoadActor("Sprite3")..{
InitCommand=cmd(y,100);
};
LoadActor("Sprite4")..{
InitCommand=cmd(y,150);
};
};
Example 2 of what not to do:
--There is no positioning code here, it's all done inside the "MyCoolActor.lua" file
t[#t+1] = LoadActor("MyCoolActor");
After reading the above, you can probably figure out why this is a bad idea.
NOOOOOO!!!!!! DO NOT DO THIS!!!!!!!! You are only making it harder for you and everyone else to debug your theme if you do this. You are making it take twice as long to implement your features if you do this. Seriously, do not do this.
t[#t+1] = LoadActor("MyCoolSprite_P1")..{
InitCommand=cmd(xy,300,SCREEN_CENTER_Y;player,PLAYER_1);
};
t[#t+1] = LoadActor("MyCoolSprite_P2")..{
InitCommand=cmd(xy,SCREEN_WIDTH-300,SCREEN_CENTER_Y;player,PLAYER_2);
};
Instead:
--This will load it only if the player is joined, so it doesn't load an unnecessary sprite and then make it invisible.
--If you want to load it anyway (in case LateJoin is enabled), use "for player in ivalues(PlayerNumber)"
for player in ivalues(GAMESTATE:GetEnabledPlayers()) do
--This loads "MyCoolSprite_P1" or "MyCoolSprite_P2"
t[#t+1] = LoadActor("MyCoolSprite_"..pname(player))..{
InitCommand=function(self)
--For player 1 it will be on the left, for player 2 it will be on the right
if player == PLAYER_1 then
self:x(300)
else
self:x(SCREEN_WIDTH-300)
end
self:y(SCREEN_CENTER_Y);
end;
};
end
You might be asking "Okay, but my code is another lua file! How do I pass in the player?". Well, that's what the second argument of LoadActor is for. Let's say you have a file named "label.lua"
t[#t+1] = LoadActor("Label");
You can supply the player (or anything, actually) as the second argument to LoadActor, where it will be passed in as a variable named ...
.
t[#t+1] = LoadActor("Label", PLAYER_1);
Then in the first line of Label.lua:
local player = ...
This is fairly obvious. Don't allocate memory if you don't need to, only do it when you need to use a variable more than once.
CurrentSongChangedMessageCommand=function(self)
local song = GAMESTATE:GetCurrentSong();
if song then --Song is only being used once
local meter = GAMESTATE:GetCurrentSteps(PLAYER_1):GetMeter(); --meter is only being used once
self:settext(meter);
end;
end;
This function could be changed to this:
CurrentSongChangedMessageCommand=function(self)
if GAMESTATE:GetCurrentSong() then
self:settext(GAMESTATE:GetCurrentSteps(PLAYER_1):GetMeter());
end;
end;
(Of course, you are free to ignore this one if you don't care about wasting memory)
cmd() has a performance penalty because it has to be translated.
Def.Quad{
OnCommand=cmd(addx,-300;linear,0.15;addx,300);
}
Use function(self) instead.
Def.Quad{
OnCommand=function(self) self:addx(-300):linear(0.15):addx(300) end;
}
(Of course, you are free to ignore this one if you don't care about causing lag)
Let's say you have a function that returns an Actor and one of the parameters for that function is the player. But CurrentStepsChanged is an old style of MessageCommand, so you think it's impossible and put both in. Don't do this.
CurrentSongChangedMessageCommand=cmd(playcommand,"Set");
CurrentStepsP1ChangedMessageCommand=cmd(playcommand,"Set");
CurrentStepsP2ChangedMessageCommand=cmd(playcommand,"Set");
CurrentTrailP1ChangedMessageCommand=cmd(playcommand,"Set");
CurrentTrailP2ChangedMessageCommand=cmd(playcommand,"Set");
The solution (assume player is a variable that is either PLAYER_1 or PLAYER_2):
["CurrentSteps"..pname(player).."ChangedMessageCommand"]=cmd(playcommand,"Set");
["CurrentTrail"..pname(player).."ChangedMessageCommand"]=cmd(playcommand,"Set");
This will ensure your actor only listens to the commands it needs.
Far too many people do this because they don't realize the Reverse() function exists.
local function GetDiffAsInt(diff)
if diff == "Difficulty_Beginner" then
return 0
elseif diff == "Difficulty_Easy" then
return 1
elseif diff == "Difficulty_Medium" then
--(you get the idea)--
end
end;
This entire function could be replaced with
local function GetDiffAsInt(d)
return Difficulty:Reverse()[d];
end;
local difficulties = {"Difficulty_Beginner", "Difficulty_Easy", "Difficulty_Medium", "Difficulty_Hard", "Difficulty_Challenge", "Difficulty_Edit"}
enums are iterable. this is the same as local difficulties = Difficulty
. In fact you don't need to declare a variable at all, because you can access a value from the enum using Enum[i] where i is the position to access. Ex. Difficulty[1]
returns "Difficulty_Beginner"
.
Enums are iterable with for loops. You don't need to copy and paste the actor you're trying to generate every time.
t[#t+1]=DrawDifList(PLAYER_2,'Difficulty_Beginner')..{
OffCommand=cmd(linear,0.25;addx,500);
};
t[#t+1]=DrawDifList(PLAYER_2,'Difficulty_Easy')..{
OffCommand=cmd(linear,0.25;addx,500);
};
t[#t+1]=DrawDifList(PLAYER_2,'Difficulty_Medium')..{
OffCommand=cmd(linear,0.25;addx,500);
};
t[#t+1]=DrawDifList(PLAYER_2,'Difficulty_Hard')..{
OffCommand=cmd(linear,0.25;addx,500);
};
t[#t+1]=DrawDifList(PLAYER_2,'Difficulty_Challenge')..{
OffCommand=cmd(linear,0.25;addx,500);
};
t[#t+1]=DrawDifList(PLAYER_2,'Difficulty_Edit')..{
OffCommand=cmd(linear,0.25;addx,500);
};
The entire thing could be shortened to
for diff in ivalues(Difficulty) do
t[#t+1]=DrawDifList(PLAYER_2,diff)..{
OffCommand=cmd(linear,0.25;addx,500);
};
end;