How to animate a tilemap - dictionary

I'm new to cocos2d and coding and I'm sure this question was asked many times already, but I want to animate a ghost in a tilemap to go up about 150 pixels when the player is next to to it. I have
`CCSprite* sprite1 = [CCSprite spriteWithFile:#"enemy.png"];
sprite1.position = ccp(464, 80);
[self addChild:sprite1];
[sprite1 runAction:[CCSequence actions:
[CCMoveBy actionWithDuration:1 position:ccp(0, 150)],
[CCMoveBy actionWithDuration:1 position:ccp(0, -200)], nil]];`
which animates a sprite but it stays on the map. If I add it to a tilemap it will only show when the player is close. But I'm not exactly sure how to do that. Thanks in advance

Don't get it on a tilemap then, you're coding custom behavior for that specific character, work it apart from the TileMap. You'll have to work on sensors if you're planning on getting efficient code, maybe Box2D or the built in Chipmunk engine that CocosV3 has recently fully integrated, or an "easy" way out of this, if you're not planning on making a sliding scenario you could work simple coordinates and an event listener on the update method, so when the "character" reaches the point you wish the ghost to appear, well then you can make that method happen.
Wrap the custom behavior on a class that you may further reuse , maybe a class named sensorSprite , once you code the default behavior for the class, you could create methods to instantiate the object with specific coordinates to generate the sensor around, or some other cool stuff.
Here's what your class could look like.
Header File
#interface SensorSprite : CCSprite {
kCharacterState currentState;
}
-(id)initWithSpriteFrame:(CCSpriteFrame *)spriteFrame inCoordinates:(CGPoint)spawnLocation withParent:(id)theParent;
-(void)updateSensorSpriteWithCharacters:(NSMutableArray*)charactersArray;
#end
Implementation File
typedef enum {
kCharacterStateAlive,
kCharacterStateDead,
kCharacterStateAppearing,
kCharacterStateDissapearing
}kCharacterState;
#import "SensorSprite.h"
#implementation SensorSprite
-(id)initWithSpriteFrame:(CCSpriteFrame *)spriteFrame inCoordinates:(CGPoint)spawnLocation withParent:(id)theParent{
self = [CCSprite spriteWithImageNamed:#"enemy.png"];
self.position = ccp(464, 80);
[theParent addChild:self];
return self;
}
-(void)updateSensorSpriteWithCharacters:(NSMutableArray *)charactersArray {
//If you're planning on having different characters to update your state from then you should use tags.
for (int i=0; i<=charactersArray.count-1; i++) {
CCSprite *characterInArray = [charactersArray objectAtIndex:i];
if (CGRectIntersectsRect([characterInArray boundingBox], [self boundingBox])) {
//What happens when the CG Rects from this class and another one intersects.
//For making reactive to encountering different sprites, you should use tags , and on each cycle detect what sprite is the one colliding by looking at it's tag.
}
}
}
-(void)changeStateTo:(kCharacterState)theState {
if (currentState==theState) {
//It's the same state.
return;
}
switch (theState) {
case kCharacterStateAlive:
//What happens when character state alive.
break;
case kCharacterStateDead:
//What happens when character state dead
break;
case kCharacterStateAppearing:
//What happens when character state reapearing
break;
case kCharacterStateDissapearing:
//What happens when character state dissapearing.
default:
break;
}
//After doing the required we set our current state to the changed state.
currentState = theState;
}
#end
Notice my comments on the code, it could be improved in huge ways, but this should be your base to understand what's going on here.

Related

How to write if statement comparing numbers that creates objects conditionally in QML

I'm working with QML and Python3.6 + PySide2 and I'm trying to write script in QML that takes two integers from a connection in python and compares them to decide what image background to use for the window.
There are a few things I'm struggling with. First, I am unsure how to compare my numeric (sunset and sunrise) variables. Second, I don't know how to write an if statement-esque part that produces a background image conditionally. Third, I don't think it's best to do this under Connections, and maybe even in my QML, but I'm not sure how to move my variables somewhere else.
I really appreciate any pointers or help!!
The data I'm drawing from looks like this:
"sunrise":1592565499,"sunset":1592617094
The QML pseudo-ish code:
Connections {
target: weather
function onDataChanged(){
if(!weather.hasError()){
var sunrise = weather.data['dt']['sunrise']
var sunset = weather.data['dt']['sunset']
if (sunrise <= sunset)
Image {
source: "night.png"}
else
Image {
course: "day.png"}
}
You cannot create QML-items from if-statements like that (neither from State's). You can call Qt.createComponent if you like, however, it is rather overkill in this example. You should directly set the source property of your image:
Image {
id: image_tod
}
Connections {
target: weather
function onDataChanged(){
if(!weather.hasError()){
var sunrise = weather.data['dt']['sunrise']
var sunset = weather.data['dt']['sunset']
if (sunrise <= sunset)
image_tod.source = "night.png"
else
image_tod.source = "day.png"
}
}
}
Looking at the code, you might actually be able to bind it directly to the source property (not sure what your model exactly looks like):
Image {
source: {
if(weather.data['dt']['sunrise'] <= weather.data['dt']['sunset'])
return "night.png"
else
return "day.png"
}
}
This works because when compiling the QML, the engine creates a dependency from every referenced variable (weather and data in this case), and re-evaluates the whole binding if any of them signals a change. To make fully use of this, you should also expose hasError as a property rather than a function (and emit whenever it changes).
Follow-up update
Yes, you can make it as wild as you want. I think you mean this:
Image {
source: {
if(weather.data['dt']['sunrise'] <= weather.data['dt']['sunset'])
return "night.png"
else if(weather.data['dt']['sunrise'] > weather.data['dt']['sunset'])
return "day.png"
else
return "" //means no image
}
}

Select multiple time slots on click

When I click on a empty time slot on fullCalendar, it draws a rectangle on that empty cell. So, If my slotDuration is 30min, the block represents 30 min. I also can drag the cursor over multiple cells and select a custom range. But what I need to do is, when the user click (not drag) on a cell, select and draw the rectangle on 2 cells (representing 1 hour). Is this possible? I cannot change the slotDuration.
If I change the snapDuration to 1 hour, it works, but sadly, I cannot change it also.
What I was looking for is a way to override the event.end but that did not work.
Update 1:
I was able to do this exposing the cellDuration property:
on fullCalendar.js:
t.setCellDuration = function (minutes) {
var duration = moment.duration(minutes, 'minutes');
var view = t.getView();
view.timeGrid.cellDuration = duration;
}
now on the renderEvent handler, I can call
element.fullCalendar("setCellDuration", 60);
It works but if there is an alternative that does not involve change fullCalendar code, it would be nice.
I think you cannot do it just modifying the properties of the calendar, but you could do it modifying the fullCalendar.js file. Yes, I know you specify it on your question, but I think there is not alternative.
Exactly the listenStop function, which resides at line 4527 at version 2.3.3
listenStop check an array call dates
dates[
{FCMoment}, //start
{FCMoment} //end
]
So, before that check, you can modify your end time as you prefer. In addition, you have to render it.
In your code, now listenStop() function should be something like:
listenStop: function(ev) {
if (dates) { // started and ended on a cell?
if (dates[0].isSame(dates[1])) {
dates[1] = dates[0].clone().add(1, 'hours'); //Now we modify the end
_this.renderSelection(dates[0], dates[1]); //And render the modified selection
view.trigger('dayClick', dayEl[0], start, ev);
}
if (isSelectable) {
// the selection will already have been rendered. just report it
view.reportSelection(start, end, ev);
}
}
}

Fahrenheit and Celsius Bidirectional Conversion in AngularJS

In AngularJS, it is straightforward how to $watch a $scope variable and use that to update another. But what is the best practice if two scope variables need to watch each other?
I have as an example a bidirectional converter that will convert Fahrenheit to Celsius, and vice versa. It works okay if you type "1" into the Fahrenheit, but try "1.1" and Angular will bounce around a little before overwriting Fahrenheit that you just entered to be a slightly different value (1.1000000000000014):
function TemperatureConverterCtrl($scope) {
$scope.$watch('fahrenheit', function(value) {
$scope.celsius = (value - 32) * 5.0/9.0;
});
$scope.$watch('celsius', function(value) {
$scope.fahrenheit = value * 9.0 / 5.0 + 32;
});
}
Here's a plunker: http://plnkr.co/edit/1fULXjx7MyAHjvjHfV1j?p=preview
What are the different possible ways to stop Angular from "bouncing" around and force it to use the value you typed as-is, e.g. by using formatters or parsers (or any other trick)?
I think the simplest, fastest, and most correct solution is to have a flag to track which field is being edited, and only allow updates from that field.
All you need is to use the ng-change directive to set the flag on the field being edited.
Working Plunk
Code changes necessary:
Modify the controller to look like this:
function TemperatureConverterCtrl($scope) {
// Keep track of who was last edited
$scope.edited = null;
$scope.markEdited = function(which) {
$scope.edited = which;
};
// Only edit if the correct field is being modified
$scope.$watch('fahrenheit', function(value) {
if($scope.edited == 'F') {
$scope.celsius = (value - 32) * 5.0/9.0;
}
});
$scope.$watch('celsius', function(value) {
if($scope.edited == 'C') {
$scope.fahrenheit = value * 9.0 / 5.0 + 32;
}
});
}
And then add this simple directive to the input fields (using F or C as appropriate):
<input ... ng-change="markEdited('F')/>
Now only the field being typed in can change the other one.
If you need the ability to modify these fields outside an input, you could add a scope or controller function that looks something like this:
$scope.setFahrenheit = function(val) {
$scope.edited = 'F';
$scope.fahrenheit = val;
}
Then the Celsius field will be updated on the next $digest cycle.
This solution has a bare minimum of extra code, eliminates any chance of multiple updates per cycle, and doesn't cause any performance issues.
This is a pretty good question and I'm answering it even though it's already accepted :)
In Angular, the $scope is the model. The model is a place to store data you might want to persist or use in other parts of the app, and as such, it should be designed with a good schema just as you would in a database for example.
Your model has two redundant fields for temperature, which isn't a good idea. Which one is the "real" temperature? There are times when you want to denormalize a model, just for access efficiency, but that's only really an option when the values are idempotent, which as you found, these aren't due to floating point precision.
If you wanted to continue using the model it would look something like this. You'd pick one or the other as the "source of truth" temperature, then have the other input as a convenience entry box with a formatter and parser. Let's say we want Fahrenheit in the model:
<input type="number" ng-model="temperatureF">
<input type="number" ng-model="temperatureF" fahrenheit-to-celcius>
And the conversion directive:
someModule.directive('fahrenheitToCelcius', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
ngModel.$formatters.push(function(f) {
return (value - 32) * 5.0 / 9.0;
});
ngModel.$parsers.push(function(c) {
return c * 9.0 / 5.0 + 32;
});
}
};
});
With this, you'd avoid the "bouncing around" because $parsers only run on user action, not on model changes. You'd still have long decimals but that could be remedied with rounding.
However, it sounds to me like you shouldn't be using a model for this. If all you want is "each box updates the other box", you can do exactly that and not even have a model. This assumes the input boxes start out blank and it's just a simple tool for users to convert temperatures. If that's the case, you have no model, no controller, and aren't even hardly using Angular at all. It's a purely view-based widget at that point, and it's basically just jQuery or jQLite. It's of limited usefulness though, since with no model it can't effect anything else in Angular.
To do that, you could just make a temperatureConverter directive that has a template with a couple of input boxes, and watches both boxes and sets their values. Something like:
fahrenheitBox.bind('change', function() {
celciusBox.val((Number(fahrenheitBox.val()) - 32) * 5.0 / 9.0);
});

Catching/Connecting QPushButtons inside a QTableWidget to a function

Im a student developer using Qt to build a GUI to help users plot specific columns of data located in multiple files. The feature I'm setting up allows users to select a file using a button in each row. So the button originally would say browse and then user clicks it to open a dialog to select a file then the button text is replaced with the file name selected. Sorry for the story; my simple attempt to add some clarity.
The problem I'm having is I'm not sure how to set a policy up for the button clicked. I'd imagine that I'd have to extend the functionality of each of the QPushButtons but I don't really know how to do that. So far I am using the following to set the cell widget.
//with row count set dimensions are set becasue column count is static
//begin bulding custom widgets/QTableWidgetItems into cells
for(int x = 0; x < ui->tableWidgetPlotLineList->rowCount(); x++)
{
for(int y = 0; y < ui->tableWidgetPlotLineList->columnCount(); y++)
{
if(y == 1)
{
//install button widget for file selection
QPushButton *fileButton = new QPushButton();
if(setDataStruct.plotLineListData.at(rowCount).lineFileName != "");
{
fileButton->setText(setDataStruct.plotLineListData.at(rowCount).lineFileName);
}
else
{
fileButton->setText("Browse...");
}
ui->tableWidgetPlotLineList->setCellWidget(x, y, fileButton);
}
I was thinking that
connect(ui->tableWidgetPlotLineList->row(x), SIGNAL(fileButton->clicked()), this, SLOT(selectPlotLineFile(x));
might do the trick but I think I'm probably going in the wrong direction here. Honestly I'm not even too sure as to where it would go...
Thanks so much for reading my post. Please let me know if there is anything lacking from this post and I will update it immediately. I'd also like to thank any contributions to this post in advance!
connect(ui->tableWidgetPlotLineList->row(x), SIGNAL(fileButton->clicked()), this, SLOT(selectPlotLineFile(x));
Is not syntactically correct for a signal/slot connection. Something like this would be more appropriate:
connect(fileButton, SIGNAL(clicked()), this, SLOT(selectPlotLineFile(x));
...
If you need access to the specific button that emited the clicked() signal than you could use the sender() function in your slot:
void selectPlotLineFile() {
QPushButton *button = dynamic_cast<QPushButton*>( sender() )
}
Now you may be wondering how you know which row to operate on. There are several different approaches, one of the easier ones being to maintain a QMap<QPushButton*, int> member variable that you can use to lookup which button belongs to which row.

Resetting target values in a composite effect

We need to be able to handle a "playable" (play/pause/seek) effect in which the nature of the effect cannot be determined at compile time.
The problem we are running into is resetting the target(s) state after the effect has completed. If we manually drag the seek slider back to the beginning, everything works fine. However, if we set the playheadTime of the composite effect back to 0, the effected targets retain their original value until the playheadTime gets to the correct position to effect the target.
Here is a simplified (as much as I could) test case with view source enabled:
http://www.openbaseinteractive.com/_tmp/PlayableEffectTest/
The problem is demonstrated if you let it play to the end, and then hit the play button to start it over.
What is the best way to go about manually resetting the target values given that the exact nature of the effect is unknown?
Many thanks for your time!
edit
I forgot to mention we are using Flex 4.5 preview release.
Have you tried:
effect.reverse()
More info
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/effects/IEffect.html
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/effects/IEffect.html#reverse()
Well it's a little kludgy, but I was able to accomplish this by calling some internal methods on the effect to capture the start values, then assigned those values to the targets on a reset.
import mx.core.mx_internal;
use namespace mx_internal;
private var _propertyChangesArray:Array;
protected function captureStartValues(effect:Object):void
{
effect.captureStartValues();
_propertyChangesArray = effect.propertyChangesArray;
}
protected function reset(effect:Object):void
{
for each(var change:PropertyChanges in _propertyChangesArray)
{
var target:Object = change.target;
for(var p:String in change.start)
{
if(target.hasOwnProperty(p))
{
var startVal:* = change.start[p];
var endVal:* = target[p];
if(!isNaN(startVal) && startVal != endVal)
{
target[p] = startVal;
}
}
}
}
effect.playheadTime = 0;
}
I don't know if this is the best way to accomplish this, but it seems to be working so far. I am absolutely open to suggestions for a better method.
Cheers!

Resources