Phaser BitmapData Sprite Immovable on Drag - phaser-framework

I am a noob with Phaser games in general but I am trying to make a scrabble like game.
I made my tiles as BitmapData and added to sprite. I want to be able to drag and drop them on my scrabble board but not be able to place one tile in a spot where another tile is.
For debugging purposes Ive been just trying to get the individual tiles to respect each other's physics when you drag and drop. The behavior I want is, when dragging, to bump into another tile with that tile "holding its ground" and the dragging tile unable to cross on top of the other tile. Currently the dragged tile just goes on top of other tiles. I've looked at a lot of examples and feel that my issue may be either because my sprite is made with bitmapData or something around the drag event...
function makeTile (tileIndex, tile) {
var bmd = game.add.bitmapData(canvasZoom, canvasZoom);
// draw to the canvas context
bmd.ctx.beginPath();
bmd.ctx.rect(0, 0, canvasZoom, canvasZoom);
bmd.ctx.fillStyle = '#efefef';
bmd.ctx.fill();
bmd.ctx.fillStyle = '#234234';
bmd.ctx.font="20px Georgia";
bmd.ctx.fillText(tile.tileName, 7,23);
// use the bitmap data as the texture for the sprite
var tileSprite = game.make.sprite((tileIndex * canvasZoom) + canvasZoom, canvasZoom, bmd);
game.physics.arcade.enable([tileSprite]);
tileHandGroup.add(tileSprite);
tileSprite.inputEnabled = true;
var bounds = new Phaser.Rectangle(canvasZoom, canvasZoom, spriteWidth * canvasZoom, (spriteHeight * canvasZoom) + (canvasZoom * 2));
tileSprite.input.boundsRect = bounds;
tileSprite.name = 'tileSpriteName' + tileIndex;
tileSprite.input.enableDrag(true);
tileSprite.input.enableSnap(canvasZoom, canvasZoom, true, false);
tileSprite.immovable = true;
tileSprite.body.moves = false;
tileSprite.events.onDragStart.add(onDragStart, this);
tileSprite.events.onDragStop.add(onDragStop, this);
}
function firstTileHand() {
tileHandGroup = game.add.physicsGroup(Phaser.Physics.ARCADE);
game.physics.enable(tileHandGroup, Phaser.Physics.ARCADE);
tileHandGroup.enableBody = true;
tileHandGroup.name = 'tileHandGroup';
for (var i = 0; i < tiles.length; i++)
{
makeTile(i, tiles[i]);
}
}

Related

How do I change the background color of a tabbed page in Xamarin iOS?

I need to change the background color of the currently tabbed page in my UITabBarController. I've searched through every stackoverflow post I could find but nothing worked for me. I thought there would be something like UITabBar.Appearance.SelectedImageTintColor, just for the background color but it doesn't seem so.
For example, I want to change the color of that part when I am on the right tab:
Does someone know how to do that?
You could invoked the following code in your UITabBarController
public xxxTabBarController()
{
//...set ViewControllers
this.TabBar.BarTintColor = UIColor.Red;
}
Update
//3.0 here is if you have three child page in tab , set it as the current value in your project
//
var size = new CGSize(TabBar.Frame.Width / 3.0, IsFullScreen());
this.TabBar.SelectionIndicatorImage = ImageWithColor(size,UIColor.Green);
double IsFullScreen()
{
double height = 64;
if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
{
if (UIApplication.SharedApplication.Delegate.GetWindow().SafeAreaInsets.Bottom > 0.0)
{
height = 84;
}
}
return height;
}
UIImage ImageWithColor(CGSize size, UIColor color)
{
var rect = new CGRect(0, 0, size.Width, size.Height);
UIGraphics.BeginImageContextWithOptions(size, false, 0);
CGContext context = UIGraphics.GetCurrentContext();
context.SetFillColor(color.CGColor);
context.FillRect(rect);
UIImage image = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return image;
}
The trick is to use the SelectionIndicatorImage Property of the UITabBar and generate a completely filled image with your desired color using the following method:
private UIImage ImageWithColor(CGSize size)
{
CGRect rect = new CGRect(0, 0, size.Width, size.Height);
UIGraphics.BeginImageContext(size);
using (CGContext context = UIGraphics.GetCurrentContext())
{
context.SetFillColor(UIColor.Green); //change color if necessary
context.FillRect(rect);
}
UIImage image = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return image;
}
To initialize everything we override ViewWillLayoutSubviews() like this:
public override void ViewWillLayoutSubviews()
{
base.ViewWillLayoutSubviews();
// The tabbar height will always be 49 unless we force it to reevaluate it's size on runtime ...
myTabBar.InvalidateIntrinsicContentSize();
double height = myTabBar.Frame.Height;
CGSize size = new CGSize(new nfloat(myTabBar.Frame.Width / myTabBar.Items.Length, height));
// Now get our all-green image...
UIImage image = ImageWithColor(size);
// And set it as the selection indicator
myTabBar.SelectionIndicatorImage = image;
}
As mentioned in this article (google translating it step by step when necessary lol) calling InvalidateIntrinsicContentSize() will force the UITabBar to reevaluate it's size and will get you the actual runtime height of the tab bar (instead of the constant 49 height value from XCode).

three.js orbit object around a changing axis

I have a simple 3D object and I am able to rotate i.e., move with my left mouse button, around the centre of axis - works fine. When I pan using the right mouse button the axis also shifts, as such it no longer moves around it’s present axis.
How can I move the object around it’s current axis, no matter where I drag the object?
Below is the complete code of script.js
var scene = new THREE.Scene();
var axisHelper = new THREE.AxisHelper(100);
scene.add(axisHelper);
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
camera.position.y = -200;
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.enableZoom = true;
var keyLight = new THREE.DirectionalLight(new THREE.Color('hsl(30, 100%, 75%)'), 1.0);
keyLight.position.set(-100, 0, 100);
var fillLight = new THREE.DirectionalLight(new THREE.Color('hsl(240, 100%, 75%)'), 0.75);
fillLight.position.set(100, 0, 100);
var backLight = new THREE.DirectionalLight(0xffffff, 1.0);
backLight.position.set(100, 0, -100).normalize();
scene.add(keyLight);
scene.add(fillLight);
scene.add(backLight);
var mtlLoader = new THREE.MTLLoader();
mtlLoader.setTexturePath('assets/');
mtlLoader.setPath('assets/');
mtlLoader.load('180319_object01.mtl', function (materials) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials(materials);
objLoader.setPath('assets/');
objLoader.load('180319_object01.obj', function (object) {
object.scale.set( 200, 200, 200 );
scene.add(object);
object.position.x = -100;
object.position.y = -100;
object.position.z = 0;
});
});
var animate = function () {
requestAnimationFrame( animate );
controls.update();
renderer.render(scene, camera);
};
animate();
It is because your camera is being moved by the controller and not the object itself, rather than update the cameras position and direction ((ie which is what the orbit controller is doing).in your render method you'll want to update position and Euler angle on the object itself to achieve the desired effect. To do this you'll want to track and update the position and angle of rotation about the (y?) Axis of the object,in the object (model) space.hope that makes sense, let me know if you need me to elaborate.

Fastest way to tile a drawing with pixi.js?

I have a computationally heavy drawing that is rendered by a pixi.js render.
I then create sprites from the renderer view in order to tile the drawing horizontally and I render the sprites on the on-screen canvas (using webgl as auto-detected)
However, I find the whole operation to be still slow. Can you tell how performances could be improved? I'm not sure if RenderTexture could be used instead, and if it would make a significant gain.
Thanks for your help, this is a code extract of my script:
var canvasRenderer = new PIXI.autoDetectRenderer(w, h, {
view : $('#canvas')[0],
clearBeforeRender : false
});
var canvasGraphics = new PIXI.Graphics();
var canvasStage = new PIXI.Container();
canvasStage.addChild(canvasGraphics);
canvasGraphics.beginFill();
var renderer = new PIXI.autoDetectRenderer(width, height);
var graphics = new PIXI.Graphics();
var stage = new PIXI.Container();
stage.addChild(graphics);
graphics.beginFill();
// Whole lotta rects
for (var i in rects) {
graphics.drawRect(
rects[i].left, rects[i].top, rects[i].width, rects[i].height
);
}
graphics.endFill();
renderer.render(stage);
for (var j = 0; j <= loops; j++) {
var sprite = PIXI.Sprite.from(renderer.view);
sprite.x = j * width;
canvasStage.addChild(sprite);
}
}
canvasGraphics.endFill();
canvasRenderer.render(canvasStage);
PIXI has a TilingSprite for exactly this purpose.
If you're drawing graphics, you should use a RenderTexture, and then put that into a TilingSprite.
var renderer = new PIXI.autoDetectRenderer(width, height);
var stage = new PIXI.Container();
//Draw your Graphics
var graphics = new PIXI.Graphics();
graphics.beginFill();
for (var i in rects) {
graphics.drawRect(
rects[i].left, rects[i].top, rects[i].width, rects[i].height
);
}
graphics.endFill();
//Create a RenderTexture to hold the Graphics.
//I don't know the size of your Graphics, so I'm making it up as 50 x 50
var texture = new PIXI.RenderTexture(new PIXI.BaseRenderTexture(50, 50));
//Render the Graphics into the Texture
renderer.render(graphics, texture);
//Create a TilingSprite from the Texture
var tilesprite = new PIXI.extras.TilingSprite(texture, renderer.width, renderer.height);
stage.addChild(tilesprite);
renderer.render(stage);

Drawing rectangle underneath PIXI.Text

In a word game I am trying to draw score as white numbers above a blue (or red) rectangle:
For example, in the above screenshot it is the number "13".
Here is my entire class Score.js (with currently hardcoded WIDTH and HEIGHT):
"use strict";
function Score(color) {
PIXI.Container.call(this);
this.interactive = false;
this.buttonMode = false;
this.visible = false;
this.bgGraphics = new PIXI.Graphics();
this.bgGraphics.beginFill(color, 1);
this.bgGraphics.drawRect(0, 0, Score.WIDTH, Score.HEIGHT);
this.bgGraphics.endFill();
this.addChild(this.bgGraphics);
this.text = new PIXI.Text('XXX', {font: '20px Arial', fill: 0xFFFFFF});
this.text.x = 1;
this.text.y = 1;
this.addChild(this.text);
}
Score.prototype = Object.create(PIXI.Container.prototype);
Score.prototype.constructor = Score;
Score.WIDTH = 36;
Score.HEIGHT = 24;
Score.prototype.setText = function(str) {
this.text.text = str;
}
I wonder, how to modify my setText() function, so that a new rectangle is drawn on each call - as a bounding rectangle for the str argument?
I have looked at the PIXI.Text.getBounds() method, but it returns a Matrix and not a Rectangle...
I think you can just use this.text.width. This has historically had some bugs associated with it, but it should be working right in the latest version.

UIScrollView horizontally scrolling menu in Sprite Kit

I am trying to make a horizontally scrolling menu in my new Sprite Kit game. With Sprite Kit being fairly new, there aren't many good tutorials for Sprite Kit. Is UIScrollView compatible with Sprite Kit? I have tried a couple of ways such as this:
UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
scroll.pagingEnabled = YES;
NSInteger numberOfViews = 3;
for (int i = 0; i < numberOfViews; i++) {
CGFloat xOrigin = i * self.view.frame.size.width;
UIView *awesomeView = [[UIView alloc] initWithFrame:CGRectMake(xOrigin, 0, self.view.frame.size.width, self.view.frame.size.height)];
awesomeView.backgroundColor = [UIColor colorWithRed:0.5/i green:0.5 blue:0.5 alpha:1];
[scroll addSubview:awesomeView];
}
This didn't work. I have never worked with UIScrollView before. Can anyone give a way of making a horizontally scrolling menu using UIScrollView? An example of what I am trying to do is in Bloons TD 5 with their menu.
I know this question is a few months old already, but I was also looking for a scrolling menu slider to select levels in my SpriteKit game. I couldn't find any code already existing and the thought of force fitting the UIScrollView into my SpriteKit game didn't appeal to me. So I wrote my own sliding menu for level selection.
I've simulated all of the motions of the UIScrollView so it looks natural, and can be configured to scroll left - right or up - down.
As a bonus I created a parallax background on the menu which can be easily swapped out with new images for a custom look.
https://github.com/hsilived/ScrollingMenu
The code for the Scrolling menu is very well commented so there shouldn't be to many integration issues.
The github project has a fully working demo with Worlds, Levels and parallax images. If you like it mark my answer as useful so I can gain some reputation :)
You can definitely combine UIKit controls with SpriteKit. From the SKScene where you want to have a UIScrollView add the scroll view to self.view, like this: [self.view addSubview:scroll]. You may want to do this in the -(void)didMoveToView:(SKView*)view method on your SKScene. Remember to remove the scroll view from the SKView once you transition out of the SKScene that is using it. You can do that in the -(void)willMoveFromView:(SKView*)view method. Don't forget to set the contentSize on the UIScrollView.
Note: If you want to have SpriteKit nodes over the UIKit controls then you will need to write your own SKSpriteNode that behaves like a UIScrollView.
Expanding on Note: If your scroll view needs to contain Sprite-kit nodes and not just UIKit views you won't be able to use UIScrollView unless you do something clever as suggested in the answer to this question here
There are many ways to obtain it, through SKCameraNode, using UIKit elements like UIScrollView or other ways..
But I want to show you how to do it simple with SpriteKit only.
Conceptually, you should build a larger SKNode that containt every single voice of your menu where each element have an equal distance from the other.
You can simulate the scrolling with an SKAction that move our node to the center of the nearest visible element.
GameViewController:
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
guard let view = self.view as! SKView? else { return }
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
view.showsPhysics = false
view.showsDrawCount = true
let scene = GameScene(size:view.bounds.size)
scene.scaleMode = .aspectFill
view.presentScene(scene)
}
}
GameScene:
class GameScene: SKScene {
var lastX: CGFloat = 0.0
var moveableArea = SKNode() // the larger SKNode
var worlds: [String]! = ["world1","world2","world3"] // the menu voices
var currentWorld:Int = 0 // contain the current visible menu voice index
var worldsPos = [CGPoint]() // an array of CGPoints to store the menu voices positions
override func didMove(to view: SKView) {
//Make a generic title
let topLabel = SKLabelNode.init(text: "select world")
topLabel.fontSize = 40.0
self.addChild(topLabel)
topLabel.zPosition = 1
topLabel.position = CGPoint(x:frame.width / 2, y:frame.height*0.80)
// Prepare movable area
self.addChild(moveableArea)
moveableArea.position = CGPoint(x:self.frame.midX,y:self.frame.midY)
// Prepare worlds:
for i in 0..<worlds.count {
// add a title for i^ world
let worldLabel = SKLabelNode.init(text: worlds[i])
self.moveableArea.addChild(worldLabel)
worldLabel.position = CGPoint(x:CGFloat(i)*self.frame.width ,y:moveableArea.frame.height*0.60)
// add a sprite for i^ world
let randomRed = CGFloat(drand48())
let randomGreen = CGFloat(drand48())
let randomBlue = CGFloat(drand48())
let randomColor = UIColor(red: randomRed, green: randomGreen, blue: randomBlue, alpha: 1.0)
let worldSprite = SKSpriteNode.init(color: randomColor, size: CGSize.init(width: 100.0, height: 100.0))
worldSprite.name = worlds[i]
self.moveableArea.addChild(worldSprite)
worldSprite.position = CGPoint(x:CGFloat(i)*self.frame.width ,y:0.0)
}
}
func tapNode(node:SKNode,action:SKAction) {
if node.action(forKey: "tap") == nil {
node.run(action, withKey: "tap")
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
let location = touch.location(in: self)
let touchedNode = self.atPoint(location)
lastX = location.x
let sequence = SKAction.sequence([SKAction.scale(to: 1.5, duration: 0.2),SKAction.scale(to: 1.0, duration: 0.1)]) // a little action to show the selection of the world
switch touchedNode.name {
case "world1"?:
print("world1 was touched..")
tapNode(node:touchedNode,action:sequence)
case "world2"?:
print("world2 was touched..")
tapNode(node:touchedNode,action:sequence)
case "world3"?:
print("world3 was touched..")
tapNode(node:touchedNode,action:sequence)
default:break
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
let location = touch.location(in: self)
let currentX = location.x
let leftLimit:CGFloat = CGFloat(worlds.count-1)
let rightLimit:CGFloat = 1.0
let scrollSpeed:CGFloat = 1.0
let newX = moveableArea.position.x + ((currentX - lastX)*scrollSpeed)
if newX < self.size.width*(-leftLimit) {
moveableArea.position = CGPoint(x:self.size.width*(-leftLimit), y:moveableArea.position.y)
}
else if newX > self.size.width*rightLimit {
moveableArea.position = CGPoint(x:self.size.width*rightLimit, y:moveableArea.position.y)
}
else {
moveableArea.position = CGPoint(x:newX, y:moveableArea.position.y)
}
// detect current visible world
worldsPos = [CGPoint]()
for i in 0..<worlds.count {
let leftLimit = self.size.width-(self.size.width*CGFloat(i))
let rightLimit = self.size.width-(self.size.width*CGFloat(i+1))
if rightLimit ... leftLimit ~= moveableArea.position.x {
currentWorld = i
}
worldsPos.append(CGPoint(x: (rightLimit + (leftLimit - rightLimit)/2), y:moveableArea.position.y))
}
lastX = currentX
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if worldsPos.count>0, moveableArea.action(forKey: "moveAction") == nil {
let moveAction = SKAction.move(to: worldsPos[currentWorld], duration: 0.5)
moveAction.timingMode = .easeInEaseOut
self.moveableArea.run(moveAction, withKey: "moveAction")
}
}
}
As you can see , with touchesBegan we can detect the touched world and select it, with touchesMoved we are able to move the moveable node to the correct position and to detect the nearest visible world and with touchesEnded we can create the smooth scrolling to automatic move to the current nearest visible voice after a touch..
Output:

Resources