I'm using libgdx and table layout in my screens.
When I add images in Table on initialize everything is fine, but when I add image to table on some event (on collision) images are placed in one column not every image in new row.
Table tableCenterLeft;
Image image1, image2;
private void init() {
tableCenterLeft = new Table();
image1 = new Image(Assets.instance.image.image1);
image1 = new Image(Assets.instance.image.image1);
createUI();
}
private void createUI() {
//if I add image here in init() image1 and image2 are added one below the other (fine)
tableCenterLeft.add(image1);
tableCenterLeft.add(image2);
}
private void onCollisionWithRock() {
// if I add in some event later in game , image1 and image2 are in same column (bad)
tableCenterLeft.add(image1);
}
Any idea ?
Thanks
You have to call tableCenterLeft.row() before tableCenterLeft.add(image1) if you want your table to have a new line as described in the documentation.
With your method createUI, the two images should be on the same row.
Related
I have two stages, one for the background image (using FillViewport to stretch the image)
Another one use Fitviewport to display game objects.
How do I remove the blackbars to display the background of the first stage ?
I tried Extendviewport for second stages but it will show the gap at the right for 1280x720 devices (how could I split the gap to left ?)
public LoginScreen(final MyGame gam) {
game = gam;
stageBG = new Stage(new FillViewport(800, 480));
textureBackground = new Texture(Gdx.files.internal("background.png"));
stageBG.addActor(new Image(textureBackground));
stage = new Stage(new ExtendViewport(640, 480, 1280, 720));
table = new Table();
textureLoginArea = new Texture(Gdx.files.internal("login-area.png"));
textureGirlGreen = new Texture(Gdx.files.internal("girl-green.png"));
Gdx.input.setInputProcessor(stage);
table.setBounds(415, 15, 374, 459);
table.setBackground(new TextureRegionDrawable(new TextureRegion(textureLoginArea)));
stage.addActor(table);
}
#Override
public void render(float v) {
stageBG.act(Gdx.graphics.getDeltaTime());
stageBG.draw();
stage.act(Gdx.graphics.getDeltaTime());
stage.getBatch().begin();
stage.getBatch().draw(textureGirlGreen, 120, 0);
stage.getBatch().end();
stage.draw();
}
#Override
public void resize(int width, int height) {
stageBG.getViewport().update(width, height, true);
stage.getViewport().update(width, height, true);
}
I think you should avoid using two stages with different viewports. You will get into more trouble. If you want a background image with an login overlay you should just use scene2d. Or just use two stages with the same viewport and scale your overlay manually.
However you could use frame buffers with blending to overdraw the black bars.
As you can see in my code below, I create a camera using the width and height of the device target. Then I create stage with the size of the camera viewport and add a Window to it (everything works fine till here in any device).
However, when add a TextButton to the Windows is not re-sized accordingly to the Stage size.
So, how can I re-size a TextButton to looks good in any device?
#Override
public void create() {
.
.
.
// Define camera our view port in meters because of Box2D
camera = new OrthographicCamera(20,
20 * (Gdx.graphics.getHeight() / (float) Gdx.graphics.getWidth()));
// create stage with the size of the viewport
stage = new Stage(new StretchViewport(camera.viewportWidth,camera.viewportHeight));
// create restart button
atlas = new TextureAtlas("data/ui/atlas.pack");
skin = new Skin(Gdx.files.internal("data/ui/menu_skin.json"), atlas);
TextButton restartButton = Util.createTextButton("Restart", skin, "red",
new ClickListener() {
#Override
public void clicked(InputEvent event, float x, float y) {
timer=timeMax;
}
});
// create a window and add the restart to it
skin.add("texture", new Texture("data/texture.png"));
WindowStyle windowStyle = new WindowStyle(new BitmapFont(), Color.WHITE, skin.newDrawable("texture"));
windowTryAgain = new Window("", windowStyle);
windowTryAgain.setMovable(false);
//windowTryAgain.padTop(20);
//stage.addActor(window);
windowTryAgain.setPosition(0, 0);
windowTryAgain.setSize(2,2);
windowTryAgain.row().fill().expandX();
windowTryAgain.add(restartButton).fill(0f, 0f);
windowTryAgain.center();
windowTryAgain.setVisible(false);
stage.addActor(windowTryAgain);
.
.
.
}
public class Util {
public static TextButton createTextButton(String text, Skin skin,
String styleName, EventListener listener) {
TextButton textButton = new TextButton(text, skin, styleName);
textButton.pad(10);
//Set height does not work in my case
//textButton.setHeight(0.1f);
textButton.addListener(listener);
return textButton;
}
}
You can make a secondary camera and attach it to your stage. To scale the table just scale the viewport size of the secondary camera
An example would be:
//Heres where you set the scale of your buttons and your table
Camera secondaryCamera = new Camera(Gdx.graphics.getWidth() * scale,
Gdx.graphics.getHeight() * scale);
Stage stage = new Stage();
Table table = new Table();
ImageButton button = new ImageButton(drawableUp, drawableDown, drawableChecked);
table.setFillParent(true);
stage.getViewport().setCamera(secondaryCamera);
table.add(ImageButton);
stage.addActor(table);
This works great when your using it for touch controls. You can use a table for the entire UI and scale it to your liking independent of your other game items;
I have simplified this code for easier reading
I have one class called LevelSelect,
one class called Game,
and one class called NavButtons.
*/
The code below i have added levelSelect MovieClip to the stage and
added level numbers to level boxes
onclick it goes to my selected level.
public class LevelSelectPage extends Game
{
public var levelSelectScreen:level_selection;
public var list:Array;
// add movieClips to L1,L2 etc...
public static var Level:String = "No Level Selected";
public static var shared:SharedObject;
public function LevelSelectPage( )
{
setUpLevelSelect();
}
public function setUpLevelSelect():void
{
shared = SharedObject.getLocal("savegame");
shared.clear(); // remove when game completed
if (shared.data.level_passed == undefined)
{
shared.data.level_passed = 3;
}
if (shared.data.playing_level == undefined)
{
shared.data.playing_level = "L0";
}
else
{
shared.data.playing_level = "L0";
}
levelSelectScreen = new level_selection();
addChild(levelSelectScreen);
AddMovieClipsForLevels();
}
public function AddMovieClipsForLevels():void
{
// array of movieClip instances... level pictures.
list = [
levelSelectScreen.L1,
levelSelectScreen.L2,
levelSelectScreen.L3
];
list.forEach(setupSquare);
}
public function setupSquare(square:MovieClip, index:int, array:Array):void
{
// add numbers on top of level images
// get numbers of the levels... as index starts from 0, add 1
var LevelNumber:Number = index + 1;
// convert number to string
var imageLevelNumber:String = LevelNumber.toString();
// set textfield // get childs instance name
var insertlevelNumber:TextField
= square.getChildByName("levelNumberText") as TextField;
// output text
insertlevelNumber.text = imageLevelNumber;
}
public function onSquareClick(me:MouseEvent):void
{
switch(me.currentTarget.name)
{
case "L1": // trace("level 1 was selected");
// startGame();
break;
}
}
}
}
public class Game
{
// imagen a blank screen, it doesn't matter
}
The code bellow - i have added the nav buttons to the game stage, and on click of the back button it outputs a trace statment
but how do i run the LevelSelectPage again instead ?
public class NavButtons
{
private var m_NavDisplayIcon:ReferenceArray;
private var m_stage:Stage;
public function NavButtons( stage:Stage )
{
// add navigation to stage
m_stage = stage;
m_NavDisplayIcon = new ReferenceArray( navDisplayFla );
var icon:navDisplayFla = new navDisplayFla( );
icon.x = 5;
icon.y = 5;
m_NavDisplayIcon.Add( icon );
m_stage.addChild( icon );
// addEventlisteners for backButton, pause game and information
icon.backIcon.addEventListener(MouseEvent.CLICK, goBackToLevelSelectPage);
icon.pauseIcon.addEventListener(MouseEvent.CLICK, pauseGame);
icon.informationIcon.addEventListener(MouseEvent.CLICK, showInformation);
}
public function goBackToLevelSelectPage( event:MouseEvent ):void
{
//var levelSelectScreen:level_selection = new LevelSelectPage();
//m_stage.addChild(levelSelectScreen);
trace("you pressed the backButton");
}
Think of the stage as a blank piece of paper onto which you can stick post-it notes. So when you begin the game, your piece of paper is blank, and you add a post-it note onto this blank "stage" that contains your level selectscreen.
You do this by adding movieclips or sprites to the stage, or better yet add one container to the stage to which you will then add all additional elements (this will make it easier later because you can remove the container and everything in it will be removed as well in one go).
Now, when the player clicks on a button on your level select screen, what you need to do is:
Remove the current post-it note that's on the paper (i.e, remove the container that has your level select screen).
Place a new post-it note that contains the level the player clicked on (i.e, now you add another container to the stage, and add all the elements of the new level into it.)
When the user wants to go back to the level select screen, all you do is again remove the post-it note that is on the paper currently, and add the post-it note that contains the level select screen.
I have a simple horizontal gallery of 24 pictures.
The idea is to show 3 pictures on the screen from all (24) and the middle one to be shown above as the big one automaticly ( like the scheme ). This means that when some of the arrows is pressed the movement of the images have to be by one. Also they have to be auto looped. So the first one can go in the middle to.
Here is my code so far. ( I didn't put the all 24 thumbnails, here are only 3 thumbnails only for the tests ) I've managed to move the thumbnails and select a big picture. But I need it to be auto selected when comes to the middle.
buttonL1_btn.addEventListener(MouseEvent.CLICK, left);
buttonR1_btn.addEventListener(MouseEvent.CLICK, right);
function left(event:Event):void{
box_mc.x -=50;
}
function right(event:Event):void{
box_mc.x +=50;
}
//imgs
img_3_big.visible = false;
box_mc.img_1.addEventListener(MouseEvent.CLICK, img1_show);
function img1_show(e:MouseEvent):void {
img_3_big.visible = true;
img_3_big.gotoAndStop(3);
}
box_mc.img_2.addEventListener(MouseEvent.CLICK, img2_show);
function img2_show(e:MouseEvent):void {
img_3_big.visible = true;
img_3_big.gotoAndStop(2);
}
box_mc.img_3.addEventListener(MouseEvent.CLICK, img3_show);
function img3_show(e:MouseEvent):void {
img_3_big.visible = true;
img_3_big.gotoAndStop(1);
}
Although a different approach than what you have already done, the way I would do it is by keeping all 24 images outside of the .swf in their own image files and keeping track of the current image index. Something similar to this (untested code):
import flash.display.Bitmap;
import flash.display.Loader;
import flash.events.MouseEvent;
var allImagePaths:Vector.<String> = new Vector.<String>(); // Holds reference to all big images
var allImageThumbPaths:Vector.<String> = new Vector.<String>(); // Holds reference to all thumbnail images
var currentImageIndex:int = 0; // Keeps track of what image is currently selected
// Loaders that would load big image and thumbnails
var loaderMain:Loader;
var loaderThumb_Left:Loader;
var loaderThumb_Middle:Loader;
var loaderThumb_Right:Loader;
SetupGallery(); // initialize gallery
function SetupGallery()
{
// Setup image loaders
loaderMain = new Loader();
loaderThumb_Left = new Loader();
loaderThumb_Middle = new Loader();
loaderThumb_Right = new Loader();
// Add loaders to existing MovieClips on stage.
imageHolderMain_mc.addChild(loaderMain);
imageHolderThumbLeft_mc.addChild(loaderThumb_Left);
imageHolderThumbMiddle_mc.addChild(loaderThumb_Middle);
imageHolderThumbRight_mc.addChild(loaderThumb_Right);
// Load image paths into a vector collection (like an array)
loadImages();
// Setup arrow button listeners
buttonL1_btn.addEventListener(MouseEvent.CLICK, showImageLeft);
buttonR1_btn.addEventListener(MouseEvent.CLICK, showImageRight);
}
function loadImages()
{
// Initialize collection of images for easier looping/displaying
allImagePaths = new Vector.<String>();
allImageThumbPaths = new Vector.<String>();
// Load up all image paths into the image array(vector).
allImagePaths.push("image1.jpg");
allImageThumbPaths.push("image1_thumb.jpg");
// ... image2 - image23 ...
allImagePaths.push("image24.jpg");
allImageThumbPaths.push("image24_thumb.jpg");
// NOTE: Consider loading paths to image files via xml
}
function showImageLeft(evt:MouseEvent)
{
currentImageIndex--;
showCurrentImage();
}
function showImageRight(evt:MouseEvent)
{
currentImageIndex++;
showCurrentImage();
}
// Call this after each arrow click, direction will be determined by currentImageIndex
function showCurrentImage()
{
// Keep current index within bounds, if out of range then "loop back"
if(currentImageIndex < 0) currentImageIndex = allImagePaths.length - 1;
if(currentImageIndex > allImagePaths.length - 1) currentImageIndex = 0;
// Set right and left thumbnail indexes based on current image
// (Assuming my logic is correct of course :)
var indexRight:int = currentImageIndex - 1;
if(indexRight < 0) indexRight = allImagePaths.length - 1; // "Loop back" if needed
var indexLeft:int = currentImageIndex + 1;
if(indexLeft > allImagePaths.length - 1) indexLeft = 0; // "Loop back" if needed
// clear out any existing images
loaderMain.unload();
loaderThumb_Left.unload();
loaderThumb_Middle.unload();
loaderThumb_Right.unload();
// set correct images based on new indexes
loaderMain.load(allImagePaths[currentImageIndex]);
loaderThumb_Left.load(allImageThumbPaths[indexLeft]);
loaderThumb_Middle.load(allImageThumbPaths[currentImageIndex]);
loaderThumb_Right.load(allImageThumbPaths[indexRight]);
}
There may be some additional things you'll have to handle, but that's the general idea.
Going with this approach you will not deal with keyframes, in fact you would want to have only one keyframe where the AS code is located and you would want to remove all images from the .swf file itself.
This approach will:
Allow for "Looping back" of images when at the end or beginning of the gallery.
Make it easier to add or remove images in the future.
Improve initial load time for the end user.
using flash and actionscript 3 I want to use a lot of png's as animation. I currently use a Loader object to load these images. But now I am wondering what the best way would be to show these animations:
1.
Every time a have to show a new png, I could use the loader to load it, just having one loader:
var image:Loader = new Loader();
And using this function everytime a new image is used as part of the animation:
image.load(new URLRequest(location));
2.
I could also create dozens of loaders, one for every image, and hide every loader except the one that I want shown. When I want to animation to continue, I hide the current one and show the next one. This would mean creating A LOT of Loader objects.
Which way would be the best? Or is there a third even better way to do this?
The Flash IDE's main purpose is precisely this... I know many people hate it to develop, and with good reasons, but I really think that the ideal tool for animating in Flash is the Flash IDE.
Do the below:
public class ImageLoader { // Singleton class for explanation
private static var imageLoader:Loader = new Loader(); // 1 loader only
private static var currentImage:Bitmap = null; // currently attached image
public static function Initialize():void {
imageLoader.contentLoaderInfo.addEventListener( Event.COMPLETE, Rsc_Loaded );
}
public static function RenderImage( url:String ):void {
imageLoader.load( new URLRequest( url ) );
}
private static function Rsc_Loaded( eEvent:Event ):void {
if ( null != currentImage ) { // if previous bitmap was attached to stage
currentImage.parent.removeChild( currentImage ); // remove it
}
currentImage = eEvent.target.content; // Bitmap is loaded
// attach the content to stage for rendering
GetMainApp().stage.addChild( currentImage );
}
}
// then inside your main app. do below
// load image 1 and add it to stage
imageLoader.load("img1.png");
// time to animate to img2.png
imageLoader.load("img2.png");
this is not the most efficient method, but you can add a cache to pre-cache all the images and just attach them to stage. Or if you are hardcore, can readup about the BitmapData class and perform your own pixel manipulation.