i'm attempting to use the Capabilities class to draw an accurately sized sprite on screen at exactly (2.5" x 5") regardless of the screen's resolution, but while i believe the code is correct, the size of the sprite is not accurate - when actually measuring it with a ruler.
function inchesToPixels(inches:Number):uint
{
return Math.round(Capabilities.screenDPI * inches);
}
var mySprite:Sprite = new Sprite();
mySprite.graphics.beginFill(0x000000, 0.5);
mySprite.graphics.drawRect(0, 0, inchesToPixels(2.5), inchesToPixels(5));
mySprite.graphics.endFill();
addChild(mySprite);
I'm not entirely sure about this, but my feeling is that the screenDPI value being returned by the Capabilities class would be the same value for two monitors running the same resolution, even if the monitors had different physical dimensions.
To illustrate, if you have two monitors, one which is 14" and the other which is 28", both displaying the same resolution of 800 x 600 pixels, that screenDPI property will return the same thing because they're both using the same resolution.
However, the number of dots in a literal, real-world inch on each screen will be different because of the physical dimensions of the monitor. So when you're running your code and measuring the on-screen Sprite you create with a ruler, it's not going to match up to real-world inches. I'm not sure how you could get around this problem (if I'm right about what's causing it), it seems like it'd be difficult.
Debu
I suggest at the start of your app telling the user "I detected your monitor is XX inches" (where XX is calculated from screenDPI), and allow the user to type in a correct monitor size.
Related
I'm creating this 2D, pixel art game. When the camera follows the player (it uses easing), on the final approach, the position gets several subpixel adjustments.
If I have smoothing ON (on my graphic assets), the graphics look good (sharp. it's pixel art) but the subpixel motion is jerky/jumpy.
If I have smoothing OFF, the subpixel motion is smooth, but the pixel art graphics look blurry.
I'm using Flash player v21. I've tried this with Starling and with Flash's display list.
You have a pixelated object that is moving in increments of less than the pixel size, but you don't want to restrict your mathematical easing to integers, or even worse, factors of 8 or what have you. The solution I am using in my project for this exact issue is posted below (I just got it working last week!)
Concept
create a driver that is controlled by the easing using floating point numbers.
Allow this driver to then control where the actual display object is rendered. We can use a constraint to only allow the display object to render on your chosen resolution.
Code Example
// you'll put these lines or equivalent in the correct spots for your particular needs.
// SCALE_UP will be your resolution control. If your pixels are 4 pixels wide, use 4.
const SCALE_UP: int = 4;
var d:CharacterDriver = new CharacterDriver();
var c:Character = new Character();
c._driver = d; // I've found it useful to be able to reference the driver
d._drives = c; // or the thing the driver drives via the linked object.
// you don't have to do this.
then when you are ready to do your easing of the driver:
function yourEase(c:Character, d:CharacterDriver):void{
c.x = Math.ceil(d.x - Math.ceil(d.x)%SCALE_UP);//this converts a floating point number into a factor of SCALE_UP
c.y = Math.ceil(d.y - Math.ceil(d.y)%SCALE_UP);
Now this will make your character move around 4 pixels at a time, but still be able to experience easing!
The bit with the modulo (%) operator is the key. For instance, 102-102%4 = 100. 103-103%4 = 100. 104-104%4 = 104.
In case anyone is confused by that, look at what 102%4 does: 4 goes into 102 25 times with a remainder of 2. so 102%4 = 2. Then 102 - 2 = 100.
In your case, since the "camera" is following the player (i.e. the background is moving, right?) then you really need to apply drivers to everything in the background instead, but it is basically the same idea.
Hope this helps.
since you specifically mentioned the "final approach" i think your problem comes from the fact that the easing equations puts your graphics at fractional coordinates, especially while getting closer to the target, but you should also notice it during the rest of the animation.
depending on the easing "engine" that you're using you should be able to set a "round values" flag, so all the coordinates set will be integer values and not fractional
if that's not possible, find a way in your display objects to round the x and y values every time they change
A little lost here. I've got two movieclips. I want one on the far left (out of reach until the user hovers over their left side of the screen) and one on the far right. I've tried playing with Screen, Capabilities, and stage.
I don't know why, but apparently my resolution 1536 by 864?
trace("Screen x = " + Capabilities.screenResolutionX); //Screen x = 1536
trace("Screen y = " + Capabilities.screenResolutionY); //Screen y = 864
I don't want to set the position of the native window to what the difference of the stage's with and height is to screenResoltionX and Y just because that number seems so off to me. I'm on a monitor with it's current resolution at 1920 x 1080. The only thing I can think of is that I have my laptop set to display only on the monitor at this time with it's main screen inactive, yet the window appears on my monitor, so i'm assuming its picking up the values from my monitor .. and my laptop's screen has no where near that weird of a resolution. Anyone know where this is coming from? I've never seen this before. The same numbers return from stage.fullScreenWidth and stage.fullScreenHeight..
I'm using Adobe AIR 17.0 to build this. Building it with an opaque window. The current size of the stage is set to 1024 x 786.
Edit: I want these objects on the edges of the user's screen. I'm trying to get a grid that represents the entire bounds of the user's screen.
When using Capabilities.screenResolutionX & Capabilities.screenResolutionY, it is reporting for the primary display (so if you have more than one monitor, regardless of which monitor your app is on, it will report back the primary monitor's resolution), and only at the time your application starts (so if you change resolution after the app starts Capabilities.screenResolutionX will not reflect the new screen resolution).
Here is a quote from the documentation: (emphasis mine)
This property does not update with a user's screen resolution and instead only indicates the resolution at the time Flash Player or an Adobe AIR application started. Also, the value only specifies the primary screen.
For your purposes, it seems like it shouldn't matter what the screen resolution is. Just use the stage.stageWidth & stage.stageHeight values since they reflect the boundaries of your actual app, regardless of which monitor it's on or if it's full screen, windowed, or scaled etc.
if(stage.mouseX < stage.stageWidth * 0.5){
//mouse is on the left hand side of the application.
}
EDIT
Based off your comment, sounds like you need to do this:
When your app first runs, do this to make it responsive/liquid:
stage.align = StageAlign.TOP_LEFT
stage.scaleMode = StageScaleMode.NO_SCALE
Then the stage.stageWidth and stage.stageHeight properties will reflect the actual window size.
You can maximize it through code with:
stage.nativeWindow.maximize();
You can listen for window sizing changes with:
stage.addEventListener(Event.RESIZE, myResizeHandler);
function myResizeHandler(e:Event):void {
//stage.stageWidth & stage.stageHeight have changed
}
I've just been able to setup a Cocos2d-x for a multiresolution environment. Ie supporting all/most of the available iPhone screen sizes.
My next step is to scrap the individual files (bunch of pngs) and use sprite atlases (aka sprite sheets) instead.
How does this work in Cocos2D-x? Does anyone have a working sample setup?
Once again, Google and existing documentation (well, I don't think that exists) fail me.
For cocos2d-x v3.5...
The cocos2d-x test classes are always a great place to look to see how things can be done with cocos2d-x.
Check out the AppDelegate.cpp in:
Cocos2d-x/cocos2d-x-3.5/tests/cpp-tests/Classes/AppDelegate.cpp.
In that class the window size is tested to see which set of assets should be loaded. That is part of the issue, deciding which assets to load.
For iOS retina, you can check the size of the screen and then set the content scale factor to either 1.0 for standard resolution or 2.0 for retina resolution.
Unfortunately, cocos2d-x doesn't seem to ever detect certain screen sizes and call Director::getInstanct()->setContentScaleFactor(2.0) for you, so I think we have to do this our self.
I have not tested the impact of setting contentScaleFactor on non-apple devices yet...
Check out the code below. Try running it in your AppDelegate::applicationDidFinishLaunching() method to get an idea of how cocos2d-x sees screen pixels and points; the result is not exactly what I expected. The output below is for an iPhone 5 device.
director->setContentScaleFactor(2);
Size winSizePoints = director->getWinSize();
Size winSizePixels = director->getWinSizeInPixels();
Size visibleSize = director->getVisibleSize();
CCLOG("Content scale factor set to 2.0.");
CCLOG("winSizePoints:%.2f,%.2f", winSizePoints.width, winSizePoints.height);
CCLOG("winSizePixels:%.2f,%.2f", winSizePixels.width, winSizePixels.height);
CCLOG("visibleSize:%.2f,%.2f", visibleSize.width, visibleSize.height);
director->setContentScaleFactor(1);
winSizePoints = director->getWinSize();
winSizePixels = director->getWinSizeInPixels();
visibleSize = director->getVisibleSize();
CCLOG("Content scale factor set to 1.0.");
CCLOG("winSizePoints:%.2f,%.2f", winSizePoints.width, winSizePoints.height);
CCLOG("winSizePixels:%.2f,%.2f", winSizePixels.width, winSizePixels.height);
CCLOG("visibleSize:%.2f,%.2f", visibleSize.width, visibleSize.height);
Output of above code:
Content scale factor set to 2.0.
winSizePoints:1136.00,640.00
winSizePixels:2272.00,1280.00
visibleSize:1136.00,640.00
Content scale factor set to 1.0.
winSizePoints:1136.00,640.00
winSizePixels:1136.00,640.00
visibleSize:1136.00,640.00
So it seems we have to check the the screen size and then choose assets.
One option is to then use code like this to decide which assets to load based on if the screen is retina or not. You could use a similar approach to also loading different size background images to deal with different aspect ratios (more aspect ratios below).
float scaleFactor = CCDirector::getInstance()->getContentScaleFactor();
if (scaleFactor > 1.99)
{
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("spriteSheet-hd.plist", "spriteSheet-hd.png");
}
else
{
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("spriteSheet.png", "spriteSheet.plist");
}
For sprite sheets, I highly recommend Texture Packer. Awesome tool that can create SD and HD sheets and .plist files for you with the press of one button.
If you publish to multiple platforms, you will also want to use the macros to detect the device type and use that information in deciding which assets to load.
e.g.
if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
elif CC_TARGET_PLATFORM == CC_PLATFORM_WP8
elif CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
elif CC_TARGET_PLATFORM == CC_PLATFORM_IOS
elif CC_TARGET_PLATFORM == CC_PLATFORM_MAC)
endif
Another thing to consider is that on low power CPU and RAM devices, loading large assets might make your frames per second (FPS) go to crap. Or worse, the device might not be able to load the asset or could crash the game. So be sure to test/profile on least common denominator devices that you plan to support.
Aspect Ration is another important issue (landscape width:height).
iPad aspect ratio is 4:3
iPhone 4 is 3:2
iPhone 5 & 6 are 16:9
Android throws in a bunch more width:height ratios.
WP8.1 is 15:9... not sure if others.
The point is that you probably don't want to stretch your backgrounds to get them to fill the screen, or have black bars on the edges where assets are not tall or wide enough.
To deal with this I create background assets that are wider than they need to be going off the right side of the screen. That content looks good but is not vital to convey the meaning of the background. That extra content can be clipped on devices that have narrower aspect ratios, i.e. ipad, and is visible on wider aspect ratio devices, i.e. iPhone 6.
The background images are then scaled up/down to match the screenSize height and retain their aspect ratio.
I have looked into the SuperKaolio example on Libgdx github repo. It is basically a test for integrating Tiled maps with Libgdx. They are using the unit scale 1/16 and if I have understood it correctly it means that the world no longer is based on a grid of pixels but on a grid of units (each 16 pixels wide). This is the code and comment in the example:
// load the map, set the unit scale to 1/16 (1 unit == 16 pixels)
map = new TmxMapLoader().load("data/maps/tiled/super-koalio/level1.tmx");
renderer = new OrthogonalTiledMapRenderer(map, 1 / 16f);
I am basically wondering why you would want to do that. I only got problems doing it and can't see any obvious advantages.
For example, one problem I had was adding a BitMap font. It didn't scale at all with the background and one pixel in the font occupied an entire unit. Image here.
I'm using this code for drawing the font. It's a standard 14 points arial font included in libgdx
BitmapFont font = new BitmapFont();
font.setColor(Color.YELLOW);
public void draw(){
spriteBatch.begin();
font.draw(batch, "Score: " + thescore, camera.position.x, 10f);
spriteBatch.end();
}
I assume there is a handy reason to have a 1/16th scale for tiled maps (perhaps for doing computations on which tile is being hit or changing tiles (they're at handy whole-number indices).
Anyway, regardless of what transformation (and thus what "camera" and thus what projection matrix) is used for rendering your tiles, you can use a different camera for your UI.
Look at the Superjumper demo, and see it uses a separate "guiCam" to render the "GUI" elements (pause button, game over text, etc). The WorldRenderer has its own camera that uses world-space coordinates to update and display the world.
This way you can use the appropriate coordinates for each aspect of your display.
I am using this function, adapted from Plastic Sturgeon (http://plasticsturgeon.com/2010/09/as3-get-visible-bounds-of-transparent-display-object/) to get the visible bounds of a display object.
public static function getVisibleBounds(source:DisplayObject):Rectangle
{
var matrix:Matrix = source.transform.concatenatedMatrix;
var data:BitmapData = new BitmapData(1000, 1000,true,0x00000000);
data.draw(source, matrix);
var bounds:Rectangle = data.getColorBoundsRect(0xFFFFFFFF,0x000000,false);
data.dispose();
return bounds;
}
However, the bounds are offset from the object, depending on the stage size. It works perfectly for the default stage size (550px×400px), but when either dimension is increased, it moves in the direction opposite to that dimension (when x is increased, it is offset from the object leftward, and when y is increased, it is offset from the object downward.) It doesn't do this consistently. The offset(stage dimension) is non-linear, as it is 0 for a certain range of stage dimensions, then for stage dimensions greater than that range, it quickly rises with the stage dimension. The offset is also different depending on what I changed the stage dimension from, e.g. if I go from 400px to 1000px in stages, testing movie in between, the boundaries are offset differently than if I go from 400px to 1000px all at once, or without testing movie at intermediate stages. Sometimes the offset only changes with one dimension, and the other dimension doesn't do anything. Also the published file is different from the test. I tried putting the function in the same file as the display object, instead of in an external file, but that's still unreliable. I wonder if there's some fix that could reliably give me the actual visible boundaries of the display object, regardless of the stage size and all this other stuff.
My computer runs Windows Vista Home Premium 32-bit, and I am using Adobe Flash Professional CS5.5.
This may be an issue that can be solved by setting some stage properties. First try setting the stage not to scale:
this.stage.scaleMode = "noScale";
Then set some alignment rules:
this.stage.align = "TL";
If that helps, it may be that your bitmap copying was running into some issues with scaling bugs.