web 2.0

Super promoção “Windows 7 salvando a sua vida”

A galera do 100loop é realmente muito empolgada! Essa semana produziram mais um super vídeo para participarem do “Desenvolvedor Windows”. Dessa vez o pessoal se superou com atuações hollywoodianas, originalidade e improviso. Falando nisso não podemos deixar de lembrar dos participantes, Luciano Lima, André Paulovich, André Castro e eu João Paulo, parabéns galera, o resultado foi muito bom.

Como foi a produção:

Depoimento de João Ferreira

Começamos tarde, já passava das 20h30m quando resolvemos testar a funcionalidade e para ser sincero… apanhamos um pouco do inglês, quando estávamos a beira de desistir da ideia o Luciano Lima finalmente conseguiu acionar o primeiro “switch Windows Messenger”, aí foi aquela alegria, com tudo funcionando perfeitamente partimos para produção. Como vocês viram o André Paulovich usou toda sua malandragem adquirida durante a adolescência com os manos da ZN e o grande André Castro mostrou que realmente é um menino bom, não leva jeito pra ladrão. Eu tentei ser o mocinho, que chegava e salvava todo mundo, tipo o Stalone nos filmes do Rambo… o máximo que consegui foi me aproximar do Fucker and Sucker.

E para quem quiser curtir, segue o vídeo.

Parabéns a todos, vocês foram demais!

Agora é torcer e acessar, vamos juntos conquistar mais essa vitória.

Tags:

Desenvolvimento | C# | General

Criando uma classe em runtime com Reflection

Fala pessoal, tudo na paz? Peço desculpas pela demora em colocar novos posts mas as coisas andam meio corridas pro meu lado, mas para compensar isso, vamos ver hoje um post muito legal.

Hoje vamos ver como podemos criar uma classe em runtime, para isso iremos utilizar Reflection (Reflexão).

Bem, esta necessidade se deu devido a um projeto que estou trabalhando e tendo em vista que talvez alguém possa passar pelo mesmo problema, vou compartilhá-lo com vocês.

A explicação está no próprio código, caso seja necessário uma explicação mais detalhada, a colocarei no final do post ok!

Listagem 01

using System;
using System.Reflection;
using System.Reflection.Emit;

namespace DynamicProject.ConsoleApp
{
    class Program
    {
        static void Main( string[] args )
        {
            //
            // Estas constantes serão dinâmicas ou seja
            // elas virão de uma fonte totalmente desconhecida
            // mas para efeito de post, irei setá-las manualmente
            //
            const string assemblyName = "MyAssembly";
            const string moduleName = "MyModule";
            const string className = "MyClass";
            const string fieldName = "_name";
            const string propertyName = "Nome";

            // 
            // Retorna o Domain da Thread corrente
            // 
            AppDomain ad = AppDomain.CurrentDomain;

            // Cria um novo assembly - Namespace
            AssemblyName an = new AssemblyName
                                  {
                                      Name = assemblyName
                                  };

            //
            // Diz que nosso assembly pode apenas ser executado.
            // Temos também a opção de gravar este assembly
            // gerando uma dll, mas esse não é o nosso caso
            //
            AssemblyBuilder ab = ad.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);

            //
            // Criamos um módulo que será responsável por 
            // guardar nosso assembly
            //
            ModuleBuilder mb = ab.DefineDynamicModule(moduleName);

            //
            // Nesta parte definimos qual(ais) atributo(s)
            // nossa classe irá possui. Aqui definimos que
            // nossa classe será pública e serializável
            //
            TypeBuilder tb = mb.DefineType(className, TypeAttributes.Public | TypeAttributes.Serializable);

            // 
            // Como estamos utilizando reflection precisamos criar
            // um campo privado para receber os valores
            // que serão setados na propriedade, isso porque pelo que
            // andei vendo, não é possível criar propriedade automática
            // simplesmente com get/set, daí a necessidade de um private field
            //
            FieldBuilder fieldBuilder = tb.DefineField(fieldName, typeof(string), FieldAttributes.Private);

            // 
            // Aqui criamos nossa Propriedade.
            // Como podem ver informamos:
            //  Nome da Propriedade
            //  Se ela vai possui um valor Default
            //  Qual o tipo (String, DateTime, Boolean, etc.)
            //  E um valor null para o ultimo parâmetro, que aliás não entendi o porque dele!!..rsrs
            //
            PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, typeof (String), null);

            // 
            // Definimos alguns atributos especiais
            // para nosso field (get e set)
            //
            MethodAttributes getSetAttribute = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;


            // 
            // Como não estamos utilizando propriedade automática
            // precisamos criar um método Get para nossa propriedade.
            //
            MethodBuilder custNameGetPropMthdBldr = tb.DefineMethod("get_" + propertyName, getSetAttribute, typeof(string), Type.EmptyTypes);

            //
            // Gera o MSIL que iremos trabalhar
            //
            ILGenerator custNameGetIL = custNameGetPropMthdBldr.GetILGenerator();

            //
            // Para o método get, utilizamos:
            //  OpCodes.Ldarg_0 - retorna o argumento com index 0 da Stack
            //  OpCodes.Ldfld - encontra o valor do campo no objeto atual
            //  OpCodes.Ret - retorna o valor de volta a pilha, caso haja valor.
            //
            custNameGetIL.Emit(OpCodes.Ldarg_0);
            custNameGetIL.Emit(OpCodes.Ldfld, fieldBuilder);
            custNameGetIL.Emit(OpCodes.Ret);

            // 
            // Assim como criamos um método get, fazemos
            // a mesma coisa para o método set.
            //
            MethodBuilder custNameSetPropMthdBldr = tb.DefineMethod("set_" + propertyName, getSetAttribute, null, new Type[] { typeof(string) });

            //
            // Gera o MSIL que iremos trabalhar
            //
            ILGenerator custNameSetIL = custNameSetPropMthdBldr.GetILGenerator();

            //
            // Para o método set, utilizamos:
            //  OpCodes.Ldarg_0 - retorna o argumento com index 0 da Stack
            //  OpCodes.Ldarg_1 - retorna o argumento com index 1 da Stack
            //  OpCodes.Stfld - altera o valor atual para o novo valor
            //  OpCodes.Ret - retorna o valor de volta a pilha, caso haja valor.
            //
            custNameSetIL.Emit(OpCodes.Ldarg_0);
            custNameSetIL.Emit(OpCodes.Ldarg_1);
            custNameSetIL.Emit(OpCodes.Stfld, fieldBuilder);
            custNameSetIL.Emit(OpCodes.Ret);

            //
            // Feito tudo isso acima, devemos informar ao nosso PropertyBuilder
            // nossos dois métodos (get/set) correspondentes.
            //
            propertyBuilder.SetGetMethod(custNameGetPropMthdBldr);
            propertyBuilder.SetSetMethod(custNameSetPropMthdBldr);

            // 
            // Para finalizar criamos a classe. 
            // Basta invocarmos o método CreateType do nosso TypeBuilder
            //  
            Type t = tb.CreateType();

            //
            // Através do método CreateInstance da class Activator
            // geramos uma instancia de nossa nova classe.
            //
            object instance = Activator.CreateInstance(t);

            //
            // Agora podemos invocar nossa propriedade
            // e setar um valor para ela.
            // Atente para os parâmetros passados para o
            // método InvokeMember, além do nome da propriedade
            // temos também uma flag SetProperty, bem como a instancia
            // que acabamos de criar e por ultimo o valor que queremos.
            //
            t.InvokeMember(propertyName, BindingFlags.SetProperty, null, instance, new object[] { "Luciano Lima" });

            //
            // O código abaixo é para testarmos nossa classe.
            // Nele varremos as propriedades para pegarmos
            // o valor que nela(s) foi(ram) setado(s).
            //
            PropertyInfo[] propertyInfo = t.GetProperties();

            foreach (var info in propertyInfo)
            {
                Console.WriteLine(info.Name + ": " + t.InvokeMember(propertyName, BindingFlags.GetProperty, null, instance, null));
            }

            Console.ReadLine();
        }
    }
}

 

Espero que tenham gostado!!

Enjoy!!

Tags:

C#

Programa Students to Business (S2B) - Novos Cursos

A PUC Minas e o Centro de Inovação Microsoft oferecem a partir de setembro o programa Students to Business (S2B), voltado para estudantes de ensino médio e superior.

O Students to Business é uma iniciativa da Microsoft, dos Parceiros da Microsoft e as principais universidades do país com o objetivo de capacitar estudantes nas áreas de Tecnologia da Informação (TI), e oferecer visibilidade e oportunidades de emprego.

O programa (S2B) tem por objetivo aproximar estudantes de oportunidades de trabalho nas carreiras de Tecnologia de Informação. Neste semestre serão oferecidos cursos, nas áreas de Infraestrutura de Sharepoint (novidade desta edição), Banco de Dados com ênfase em Business Intelligence e Desenvolvimento de Sistemas.

A capacitação é dividida em 3 fases, possibilitado aos estudantes a oportunidade de  adquirirem formação técnica para tornarem-se profissionais júnior em TI. Na primeira fase o curso traz informações sobre as carreiras de TI, na segunda, aulas teóricas e na terceira, aulas práticas, com o desenvolvimento de um projeto tecnológico. No encerramento são entregues certificados, em um evento que inclui uma feira de empregos.

Para participar do programa Students to Business acesse o site www.programas2b.com.br e faça sua inscrição até o dia 19/09!

Local do Students to Business:

As capacitações acontecem na unidade São Gabriel da PUC Minas (Rua Walter Ianni, 255 – bairro São Gabriel), onde está instalado o Centro de Inovação Microsoft – MIC BH. O núcleo é uma parceria entre a PUC Minas, a Microsoft e a empresa de tecnologia da informação BHS e tem o objetivo de fomentar o uso de tecnologias de ponta pelas empresas da região e oferecer capacitação profissional, além de estimular a indústria local de software.  Informações: (31) 3439.5217.

Centro de Inovação Microsoft de Belo Horizonte (MIC BH)

Microsoft Innovation Center - Belo Horizonte – MG

PUC Minas - São Gabriel

Tel.:(31) 3439-5217

Site: http://www.micbh.com.br

Tags:

C# | Asp .Net | Desenvolvimento Web

Asp .Net MVC 3–Preview 1

Fala galera tudo tranks?

Antes de darmos continuidade aos posts sobre WCF, gostaria de apresentar a vocês o mais novo preview do Asp Net MVC 3.

Para que vocês possam acompanhar o post da melhor forma possível, abaixo segue o link para download do primeiro preview, clique aqui.

O Asp .NET MVC 3 será compatível com a sua versão anterior, o Asp .NET MVC 2, contudo, isso não significa que será fácil atualizar os projetos que você está escrevendo com o MVC 2 para o MVC 3 quando eles lançarem a versão final.

As funcionalidades do Asp .Net MVC 3 tem como base as funcionalidades do Asp .Net MVC 1 e 2, ou seja, todo seu conhecimento adquirido em sites e livros não serão perdidos, isso porque, as funcionalidades do Asp .Net MVC 1 e 2 não se tornaram obsoletas.

Ok Luciano, mas a pergunta de todos é: “Posso instalar o Asp .Net MVC 3 junto com o ASP.NET MVC 2?”, a resposta é sim, você pode, inclusive a versão preview, sem que esta impacte projetos existentes da versão do Asp .Net MVC 2 que você esteja trabalhando, eles continuarão a usar o Asp .Net MVC 2 a menos que você modifique os projetos para que estes passem a usar o Asp .Net MVC 3.

Quando você instalar a versão "Preview 1", você terá um novo conjunto de templates de projeto para o Asp.Net MVC 3 que aparecerão na janela de diálogo de Novo Projeto do Visual Studio 2010.

Vamos ver algumas das novas funcionalidades e capacidades do Asp .Net MVC 3. Ressalvo que tudo que fizermos aqui são funcionalidades da versão 3 do Asp Net MVC, isso não quer dizer que novas funcionalidades não possam sair em versões futuras.

View

O Asp.Net MVC 3 "Preview 1" inclui um conjunto de melhorias específicas para a View.

Janela de Diálogo
Foi criada uma nova janela de diálogo "Add->View", que torna mais fácil sua escolha quanto a sintaxe a ser utilizada. Ela permite que você selecione qualquer um dos motores de exibição disponíveis que estejam instalados na sua máquina, o que lhe dá a habilidade de usar qualquer abordagem de modelagem de visão que pareça mais natural para você.

mvc3_01
Figura 01 – Nova janela Add View

Nova Engine Razor
Não irei entrar em detalhes sobre a Engine Razor, isso porque daria outro post, então, sugiro que vocês deem uma lida no post do Scott Guthrie sobre o Razor

 

Controller

Assim como para a View, o Controller também ganhou algumas novidades:

Filtros Globais
O Asp.Net MVC 3 suporta a capacidade de aplicar de forma declarativa lógica "contínua" usando um mecanismo chamado de "filtros". Você pode especificar filtros nos controladores e métodos de ação hoje usando uma sintaxe de atributo conforma a listagem 1.

Listagem 01 – Filtros

[HandleError]
[LoaderOptimization(128)]
public ActionResult Index()
{
    ViewModel.Message = "Aplicação Asp.Net MVC 3";

    return View();
}

Muitas das vezes precisamos aplicar filtros a todos os controladores de uma aplicação, com o Asp .Net MVC 3, foi adicionado um método chamado RegisterGlobalFilters que irá adicionar os filtros à coleção GlobalFilters. Basta fazer a chamada no Application_Start() do seu Global.asax.

Listagem 02 – Filtros Globais

public static void RegisterGlobalFilters( GlobalFilterCollection filters )
{
    filters.Add( new HandleErrorAttribute() );
}

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters( GlobalFilters.Filters );
    RegisterRoutes( RouteTable.Routes );
}

O Asp .Net MVC 3 é flexível no que diz respeito a filtros, de modo que você pode configurar um filtro global que será aplicado condicionalmente apenas se forem respeitadas determinadas condições. Por exemplo, se a depuração estiver habilitada; se uma requisição usar um verbo http dentre outras. Os filtros também podem ser resolvidos a partir de um Conteiner.

ViewModel Dinâmico
Os Controladores do Asp.Net MVC suportam uma propriedade "ViewData" que permite a você transmitir dados para uma View usando uma coleção (Dictionary) como na listagem 03.

Listagem 03

ViewModel["Message"] = "Aplicação Asp.Net MVC 3";

Já na versão 3 do MVC, foi adotada a propriedade dinâmica, dynamic, ela permite que você use o novo
suporte à linguagem dinâmica tanto do VB quanto do C# para passar itens da ViewData usando uma sintaxe um pouco mais limpa do que aquela utilizada com a API de dicionário atual. Observe a figura 2, a propriedade Message só será conhecida em tempo de execução.

mvc3_02

Figura 02 – Dinamismo

 

ActionResult
As novidades não param por ai! O Action Result também ganhou novas funcionalidades:

HttpNotFoundResult

A nova classe HttpNotFoundResult é usada para indicar que um recurso solicitado pela URL atual não foi encontrado. Ela retorna um código de status HTTP 404 para o cliente que realizou a chamada. Opcionalmente, você pode usar o novo método de ajuda HttpNotFound() no controlador para retornar uma instância desse tipo de resultado de ação, conforme a listagem 4.

Listagem 04

public ActionResult RecuperaProduto(int codProduto)
{
    var prod = new Produto.Find(codProduto);

    if ( prod == null )
        return HttpNotFound();

    return View();
}

Redirecionamentos Permanentes

A classe HttpRedirectResult tem uma nova propriedade "Permanent" do tipo booleano que é usada para indicar se um redirecionamento permanente deve ocorrer. Um redirecionamento permanente usa o código de status HTTP 301. Em conjunto com esta mudança, a classe Controller tem agora três novos métodos para realizar redirecionamentos permanentes: RedirectPermanent(), RedirectToRoutePermanent(), e RedirectToActionPermanent().

Esses métodos retornam uma instância do tipo HttpRedirectResult com a propriedade Permanent definida com true.

HttpStatusCodeResult

A nova classe HttpStatusCodeResult pode ser usada para definir um código de status e descrição de resposta de forma explícita.

 

Ajax e Javascript
Dando continuidade às novidades, o Asp.Net MVC 3 inclui suporte para JSON, o que permite que os métodos recebam dados codificados no formato JSON podendo assim, vinculá-los como parâmetros dos métodos.

Observe o código da listagem 5, ele define um manipulador de eventos "save" que será chamado quando um botão salvar for clicado no lado cliente. O código dentro do manipulador de eventos irá criar um objeto "produto" no código JavaScript com dois campos, cujos valores são obtidos a partir de elementos de entrada HTML. Em seguida, ele usa o método .ajax() da jQuery para postar (POST) uma requisição baseada em JSON que contém o produto para uma URL /Store/UpdateProduct no servidor.

Listagem 05 – método Ajax

$("#save").click(function () {
    var prod = {
        NomeProduto: $("#ProdName").val(),
        Valor: $("#value").val()
    };

    $.ajax({
        url: "/Store/UpdateProduct",
        type: "Post",
        data: JSON.stringify(prod),
        dataType: "json",
        contentType: "application/json; charset=utf-8",
        sucess: function () { $("message").html("Produto atualizado").fadeIn() },
        error: function () { $("message").html("Erro ao atualizar o Produto").fadeIn() }
    });

    return false;
});

No lado servidor você terá algo como na listagem 6.

Listagem 06 – Atualizar Produto

public ActionResult UpdateProduct(Produto produto)
{
    //Implementar a lógica para atualização de um produto
    return null;
}

 

Validação do Modelo

No Asp.Net MVC 2, houveram algumas melhorias no que diz respeito à validação, no Asp .Net MVC 3,  este trabalho foi estendido e ainda por cima eles adicionaram suporte para vários outros novos recursos de validação e os introduziram no namespace System.ComponentModel.DataAnnotations do .NET 4.  São eles:

  • Suporte aos novos atributos de metadados – DataAnnotations, tais como DisplayAttribute.
  • Suporta as melhorias feitas na classe ValidationAttribute no .NET 4. A classe ValidationAttribute foi melhorada no .NET 4 para suportar uma sobrecarga nova IsValid que fornece mais informações sobre o contexto de validação corrente, permitindo saber por exemplo qual é o objeto que está sendo validado. Isso permite cenários mais ricos, onde você pode validar o valor atual baseando-se em outra propriedade do modelo.
  • Suporta à nova interface IValidatableObject introduzida no .NET 4. A interface IValidatableObject permite a execução de validação no nível do modelo, e permite que você forneça mensagens de erro de validação específicas para o estado geral do modelo, ou entre duas propriedades do modelo.

Na listagem 7, podemos ver como utilizar a a interface IValidatableObject nativa no .NET 4 para implementar um método de validação personalizado em uma classe.

Listagem 07 – Implementando IValidatableObject

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
    if ( string.IsNullOrEmpty( NomeProduto ) )
        return (IEnumerable<ValidationResult>) new ValidationResult("Você deve preencher o campo Nome do Produto");
    return null;
}

Além da interface IValidatableObject, o Asp.Net MVC 3 também apresenta uma nova interface chamada IClientValidatable que permite descobrir em tempo de execução se um validador tem suporte para a validação no lado do cliente. Esta interface foi projetada de modo que possa ser integrada com uma variedade de frameworks de validação.

 

Dependency Injection

O Asp.Net MVC 3 nos dá um melhor suporte para aplicação de injeção de dependência (DI) e integração com Conteiners de injeção de dependência.

Para a versão "Preview 1", foram adicionados suporte para injeção de dependência nos seguintes locais:

  • Controladores (registro e injeção de fábricas de controladores, injeção de controladores)
  • Visões (registro e injeção de motores de visão, injeção de dependências em páginas de visão)
  • Filtros de Ação (localização e injeção de filtros)

Para futuras versões, poderão ser acrescentados os seguintes suporte para injeção de dependência em:

  • Model Binders - Vinculadores de Modelo (registro & injeção)
  • Value Providers - Provedores de Valor (registro & injeção)
  • Validation Providers - Provedores de Validação (registro & injeção)
  • Model metadata Providers - Provedores de Metadados de Modelo (registro & injeção)

Ufa, finalmente terminei, acho que vocês não viam a hora né!!

Como podem ver, as melhorias são muitas, isso na versão Preview 1 heim!! Imagina o que vem por ai. Espero poder postar a vocês tudo que for novidades no Asp .Net MVC 3.

É isso ai galera, até o próximo post!

 

Enjoy!

Tags:

Asp .Net | MVC 3 | C#

C# Extension Methods

Outro dia estava trabalhando em um projeto onde precisava criar alguns métodos a mais para uma dll que havia sido gerada para um outro aplicativo. Porém, como não tinha o código fonte dela estava prestes a escrever uma nova classe e implementar os métodos que estava precisando, mas antes de começar a codificar, me lembrei que a partir da versão 3.0 do Net Framework, a Microsoft incluiu uma funcionalidade muito bacana chamada Extension Methods.

Mas o que são Extension Methods?

Basicamente é uma forma de você “inserir” código em classes já compiladas mesmo que estas sejam seladas (sealed) como a maioria das classes do .Net. Desta forma podemos incrementar nossas classes com métodos que criamos sem a necessidade de realizar uma recompilação da mesma.

Mas para que isso fique mais claro, vamos ao exemplo.

Crie um projeto do tipo C# -> Console Application, dê o nome de “ExtensionMethods”.

Agora crie um novo projeto do tipo C# –> Class Library, e dê o nome dele de MyClass. Agora renomeie a Class1.cs para Client.cs. Seu projeto deverá estar como na figura 01.

extension_01
Figura 01 – Solution Explorer

 

Agora, abra o arquivo Client.cs e adicione o código da listagem 01.

Listagem 01 – class Client.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyClass
{
    public sealed class Client
    {
        public string Name { get; set; }
        public string Email { get; set; }

        public bool EmailIsValid()
        {
            return !string.IsNullOrEmpty(Email);
        }
    }
}

Observem que criamos duas propriedades e um método que “valida” o email do cliente.

Vamos criar nossa chamada à classe Client. Abra o arquivo Program.cs e insira o código da listagem 02 no método Main da classe.

Listagem 02 – chamada a classe Cliente

var cl = new Client
              {
                 Email = "lima@lucianolima.com.br",
                 Name = "Luciano Lima"
              };

    Console.WriteLine( cl.EmailIsValid() ? "Email Válido" : "Email Inválido" );
    Console.ReadLine();

 

Ao executar este código ele irá apenas escrever Email Válido porque a propriedade Email está preenchida.

Bem, digamos que você precise realizar a validação do Nome do Cliente, mas sem ter que mexer na classe Client, é ai que Extension Methods entra em ação.

Adicione uma nova classe no projeto ExtensionMethods, dê o nome a ela de “ClientExtension.cs” e insira o código da listagem 03.

Listagem 03 – class extension

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MyClass;

namespace ExtensionMethods
{
    public static class ClientExtension
    {
        public static bool NameIsValid(this Client client)
        {
            return !string.IsNullOrEmpty(client.Name);
        }
    }
}

O importante nessa classe é a forma como criamos o método, observem que o segredo para a funcionalidade de extensão é a palavra “this” que faz referencia à classe que queremos agregar valor, o restante é código puro e simples.

Agora quando for utilizar a funcionalidade, o Visual Studio irá detectar que este método é uma extensão para a classe Client, conforme figura 02 e figura 03.

extension_02

Figura 02 – método estendido na classe Client

extension_03

Figura 03 – ToolTip mostrando que o método é uma extensão.

 

Extension Methods como falei acima pode ser aplicado a todas as classes do dot Net, então caso precise de um método exclusivo para trabalhar com string basta criar sua classe de extensão e implementar o método que achar necessário. Mas cuidado, a manutenção deste tipo de código pode ser um pouco árdua por isso, pense bem antes de sair criando extension methods.

 

Enjoy!!

Tags:

C# | Desenvolvimento

Programação Paralela em C# 4.0

Fala galera, finalmente arrumei um tempinho e consegui publicar mais este post! Tá osso!!

Hoje vamos ver mais uma novidade do .Net Framework 4.0, Parallel Programming ou como conhecemos Programação Paralela.

Mas antes de vermos como funciona vamos dar uma olhada no que é a programação paralela!

Programação Paralela é uma forma de programação em que vários cálculos são realizados simultaneamente, operando sob o princípio de que grandes problemas podem ser divididos em problemas menores, que então são resolvidos em paralelo.

A técnica de paralelismo já é empregada por vários anos, principalmente na computação de alto desempenho, mas recentemente o interesse no tema cresceu devido às limitações físicas que previnem o aumento de freqüência de processamento. Com o aumento da preocupação do consumo de energia dos computadores, a computação paralela se tornou o paradigma dominante nas arquiteturas de computadores sob forma de processadores multinúcleo.

Tradicionalmente, o software tem sido escrito para ser executado sequencialmente. Para resolver um problema, um algoritmo é construído e implementado como um fluxo serial de instruções. Tais instruções são então executadas por uma unidade central de processamento de um computador. Somente uma instrução pode ser executada por vez; após sua execução, a próxima então é executada.

Por outro lado, a computação paralela faz uso de múltiplos elementos de processamento simultaneamente para resolver um problema. Isso é possível ao quebrar um problema em partes independentes de forma que cada elemento de processamento pode executar sua parte do algoritmo simultaneamente com outros. Os elementos de processamento podem ser diversos e incluir recursos como um único computador com múltiplos processadores, diversos computadores em rede, hardware especializado ou qualquer combinação dos anteriores.

Para que possamos trabalhar com a programação paralela, a Microsoft introduziu no .Net Framework 4.0 uma nova biblioteca chamada Task Parallel Library ou TPL, que irá dividir as tarefas em múltiplos processadores sem a necessidade de utilizar Threads ou Locks.

Devemos utilizar a classe Parallel do namespace System.Threading.Task, com isso a própria classe se encarrega de escalar a tarefa nos vários núcleos disponíveis.

Para que você possa entender melhor, vamos ver alguns exemplos.

Utilizando o método Parallel.For()

Não abordarei a criação de projetos visto que todos são do tipo Console Application.

Listagem 01 – execução normal

private static void NormalFor()
{
    var sw = new Stopwatch();
    sw.Start();

    for (int i = 0; i < 500; i++)
    {
        Console.WriteLine("Thread ID = {0} - x = {1}", Thread.CurrentThread.ManagedThreadId, i);
        Thread.Sleep(100);
    }

    Console.WriteLine("Tempo de execução: {0} ms", sw.ElapsedMilliseconds);
    sw.Stop();
}
 

Listagem 02 – utilizando programação paralela

private static void ParallelFor()
{
    var sw = new Stopwatch();
    sw.Start();

    Parallel.For(0, 500, WriteX); 
    
    Console.WriteLine( "Tempo de execução: {0} ms", sw.ElapsedMilliseconds );
    Thread.Sleep(10000);
    sw.Stop();
}

private static void WriteX(int x)
{
    Console.WriteLine("Thread ID={0} - x={1}", Thread.CurrentThread.ManagedThreadId, x);
    Thread.Sleep(100);
}

 

Conforme podem observar na Figura 01 o processamento ocorre somente em uma thread, já na Figura 02 são utilizadas várias threads para realizar o processamento o que diminui o tempo de execução do método, temos então, uma melhora significativa na performance.

parallel_01
Figura 01 – execução normal

parallel_02
Figura 02 – execução paralela

 

Vamos ver um exemplo um pouco mais complexo porém que ilustra bem o paralelismo.

Utilizamos além do método For o ForEach.

Calculando PI

O número IP é chamado um número transcendental. Isto significa que ele tem um decimal infinito repetindo que não mostra nenhum padrão. Matemáticos ao redor do mundo já tentaram encontrar um padrão para ele, porém até o momento sem sucesso.

Na figura 03 temos a fórmula matemática para calculo do PI.

PI
Figura 03 – Fórmula para cálculo do PI

Na listagem 01 temos uma implementação de cinco métodos que utilizam algoritmos normais e programação paralela para calculo do PI.

Criem um projeto do tipo Console Application utilizando o framework 4.0 e insira o código da Listagem 03.

Ao executarem o código verão que o tempo de resposta é menor nos métodos que utilizam a programação paralela.

 

Listagem 03

using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

namespace ConsoleParallel
{
    internal class Program
    {
        private const int NumSteps = 100000000;

        private static void Main()
        {
            while (true)
            {
                Time(SerialLinqPi);
                Time(ParallelLinqPi);
                Time(SerialPi);
                Time(ParallelPi);
                Time(ParallelPartitionerPi);

                Console.WriteLine("----");
                Console.ReadLine();
            }
        }

        private static void Time(Func<double> work)
        {
            Stopwatch sw = Stopwatch.StartNew();
            double result = work();
            Console.WriteLine(sw.Elapsed + ": " + result);
        }


        private static double SerialLinqPi()
        {
            const double step = 1.0/NumSteps;
            return (from i in Enumerable.Range(0, NumSteps)
                    let x = (i + 0.5)*step
                    select 4.0/(1.0 + x*x)).Sum()*step;
        }


        private static double ParallelLinqPi()
        {
            const double step = 1.0/NumSteps;
            return (from i in ParallelEnumerable.Range(0, NumSteps)
                    let x = (i + 0.5)*step
                    select 4.0/(1.0 + x*x)).Sum()*step;
        }


        private static double SerialPi()
        {
            double sum = 0.0;
            const double step = 1.0/NumSteps;
            for (int i = 0; i < NumSteps; i++)
            {
                double x = (i + 0.5)*step;
                sum = sum + 4.0/(1.0 + x*x);
            }
            return step*sum;
        }


        private static double ParallelPi()
        {
            double sum = 0.0;
            const double step = 1.0/NumSteps;
            var monitor = new object();
            Parallel.For(0, NumSteps, () => 0.0, (i, state, local) =>
                                                      {
                                                          double x = (i + 0.5)*step;
                                                          return local + 4.0/(1.0 + x*x);
                                                      }, local => { lock (monitor) sum += local; });
            return step*sum;
        }

        private static double ParallelPartitionerPi()
        {
            double sum = 0.0;
            const double step = 1.0/NumSteps;
            var monitor = new object();
            Parallel.ForEach(Partitioner.Create(0, NumSteps), () => 0.0, (range, state, local) =>
                                                                              {
                                                                                  for (int i = range.Item1; i < range.Item2; i++)
                                                                                  {
                                                                                      double x = (i + 0.5)*step;
                                                                                      local += 4.0/(1.0 + x*x);
                                                                                  }
                                                                                  return local;
                                                                              }, local => { lock (monitor) sum += local; });
            return step*sum;
        }
    }
}

 

Como puderam observar, o paralelismo reduz significamente o tempo de processamento. Em alguns casos onde os laços ou cálculos forem muito simples talvez você não perceba a diferença entre a execução do algoritmos em modo “normal” ou em paralelo.

Espero que tenham entendido e gostado!!!

Tags:

C# | Programação Paralela

WCF Data Service – Parte 2

Na primeira parte de nosso post sobre WCF Data Service vimos como criar nosso serviço e executar algumas pesquisas utilizando URI. Hoje veremos como consumir nosso serviço além de conhecermos um pouco mais sobre a liberação de acesso às entidades.

Abram o projeto anterior, vamos começar dando uma olhada nas permissões de acesso de nossas entidades.

No trecho de código da listagem 01 definimos que todas as entidades receberão livre acesso, ou seja, o chamador do serviço terá total acesso a elas tanto para leitura quanto para escrita.

Listagem 01

config.SetEntitySetAccessRule("*", EntitySetRights.All);

 

O método SetEntityAcessRule recebe dois parâmetros, no primeiro deles nós informamos qual será a entidade e no segundo qual o tipo de permissão que iremos liberar. Abaixo podemos ver cada item da enum de permissões.

EntitySetRights Enumerator

All – permite total acesso à entidade (escrita/leitura)
AllRead – permite somente leitura
AllWrite – permite somente escrita
None – nega qualquer tipo de acesso
ReadMultiple – leitura de várias linhas é permitida
ReadSingle – permite que apenas uma linha da tabela seja lida
WriteAppend – permite a criação de novos dados na tabela
WriteDelete – permite a exclusão de dados
WriteMerge – permite que se faça um merge com atualização dos dados
WriteReplace – permite que se faça uma atualização de troca de dados

Agora que já conhecemos um pouco sobre o acesso às entidades, vamos criar nosso aplicativo.

Adicione um novo projeto do tipo Console Application, dê o nome dele de AppWCFDataService, agora adicione uma referencia ao serviço, Add Service Reference, clique em Discover que o Visual Studio irá procurar nosso serviço dentro da solução.

wcf_p02_01

Figura 01 – adição do serviço ao projeto

 

Abra a class Program.cs, antes de utilizamos nosso serviço devemos criar duas instancias que são DataServiceContext que faz parte do namespace System.Data.Services.Client e NorthwindEntities que é no nome da classe que contem nosso mapeamento, lembra? Criamos ele em nosso primeiro post. Caso não recorde acesse o post aqui.

Listagem 02 – Classe Program

using System;
using System.Data.Services.Client;
using AppWCFDataService.WCFDataService;

namespace AppWCFDataService
{
    class Program
    {
        static void Main( string[] args )
        {
            var context = new DataServiceContext( new Uri( "http://localhost:26482/WcfDataService1.svc" ) );
            var db = new NorthwindEntities( new Uri( "http://localhost:26482/WcfDataService1.svc" ) );
        }
    }
}

Observem que informamos a mesma URI para as duas instâncias, isso porque ambas compartilham do mesmo serviço. Esta URI foi obtida executando o serviço, este é o endereço que acesso o serviço em minha maquina, atente para passar a url correta de sua maquina.

Para testar nosso aplicativo vamos criar os métodos: ListaClientes(), InserirClienteContext(Custormer customer), InserirClienteDB(Customer customer), AtualizarCliente(Customer customer) e ApagarCliente(Customer customer).

Crie os métodos conforme o código da listagem 03.

Listagem 03

/// <summary>
/// Lista todos os clientes
/// </summary>
/// <returns></returns>
static IEnumerable<Customer> ListaClientes()
{
    var result = from customers in _db.Customers select customers;
    return result.ToList();
}

/// <summary>
/// Insere um novo cliente
/// Utiliza o context para inserir o objeto
/// </summary>
/// <param name="customer"></param>
static void InserirClienteContext(Customer customer)
{
    _context.AddObject("Customers", customer);
    _context.SaveChanges();
}

/// <summary>
/// Insere um novo cliente
/// Utiliza o mapeamento para inserir o objeto
/// </summary>
/// <param name="customer"></param>
static void InserirClienteDB( Customer customer )
{
    _db.AddToCustomers(customer);
    _db.SaveChanges();
}

/// <summary>
/// Atualiza um cliente
/// </summary>
/// <param name="customer"></param>
static void AtualizarCliente( Customer customer )
{
    var customerToUpdate = (from cus in _db.Customers where cus.CustomerID == customer.CustomerID select cus).First();
    customerToUpdate.CompanyName = customer.CompanyName;
    
    _db.UpdateObject( customerToUpdate );
    _db.SaveChanges();
}

/// <summary>
/// Exclui um cliente
/// </summary>
/// <param name="customer"></param>
static void ApagarCliente( Customer customer )
{
    var customerToDelete = ( from cus in _db.Customers where cus.CustomerID == customer.CustomerID select cus ).First();

    _db.DeleteObject( customerToDelete );
    _db.SaveChanges();
}

Agora só precisamos criar um cliente e testar nossos métodos. O código da listagem 04 mostra como isso pode ser feito.

Listagem 04

static DataServiceContext _context = new DataServiceContext( new Uri( "http://localhost:26482/WcfDataService1.svc" ) );
static NorthwindEntities _db = new NorthwindEntities( new Uri( "http://localhost:26482/WcfDataService1.svc" ) );

    static void Main( string[] args )
    {
        //
        // Lista os clientes
        //
        foreach (Customer customer in ListaClientes())
        {
            Console.WriteLine(customer.ContactName + " - " + customer.Country);
        }

        //
        // Cliente a ser inserido
        //
        var newCustomer = new Customer
                           {
                                CustomerID = "LUCLI",
                                Address = "Rua Teste",
                                City = "Belo Horizonte",
                                CompanyName = "Tabajara S/A",
                                ContactName = "Luciano Lima",
                                ContactTitle = "Sr",
                                Country = "Brasil",
                                Fax = "31 1234-5678",
                                Phone = "31 1234-5678",
                                PostalCode = "12330-123",
                                Region = "MG"
                           };

            //Inserir - Context
            InserirClienteContext(newCustomer);

            //Inserir - Entity
            InserirClienteDB(newCustomer);

            //Atualizar
            AtualizarCliente(newCustomer);

            //Excluir
            ApagarCliente(newCustomer);

            Console.ReadLine();
    }

 

Bem pessoal, vimos aqui como o trabalho com serviços está ficando cada vez mais fácil, como viram com pouco código conseguimos montar um serviço e criar uma aplicação simples para consumí-lo.

Tags:

C# | WCF

WCF Data Service

Antes de iniciarmos com WCF Data Service vamos dar uma passada rápida no conceito de Web Service e WCF para que possamos entender melhor o WCF Data Service.

Web Service é uma solução utilizada na integração de sistemas e na comunicação entre aplicações diferentes. Com esta tecnologia é possível que novas aplicações possam interagir com aquelas que já existem e que sistemas desenvolvidos em plataformas diferentes sejam compatíveis. Os Web services são componentes que permitem às aplicações enviar e receber dados em formato XML. Cada aplicação pode ter a sua própria "linguagem", que é traduzida para uma linguagem universal, o formato XML.

Essencialmente, o Web Service faz com que os recursos da aplicação do software estejam disponíveis sobre a rede de uma forma normalizada. Outras tecnologias fazem a mesma coisa, como por exemplo, os browsers da Internet acedem às páginas Web disponíveis usando por norma as tecnologias da Internet, HTTP e HTML. No entanto, estas tecnologias não são bem sucedidas na comunicação e integração de aplicações. Existe uma grande motivação sobre a tecnologia Web Service pois possibilita que diferentes aplicações comuniquem entre si e utilizem recursos diferentes.

Utilizando a tecnologia Web Service, uma aplicação pode invocar outra para efetuar tarefas simples ou complexas mesmo que as duas aplicações estejam em diferentes sistemas e escritas em linguagens diferentes. Em outras palavras, os Web Services fazem com que os seus recursos estejam disponíveis para que qualquer aplicação cliente possa operar e extrair os recursos fornecidos pelo Web Service.

Windows Communication Foundation (WCF) é um modelo de programação unificado e ambiente de execução (Framework) criado pela Microsoft que visam a construção de aplicações orientadas a serviços (Service Oriented Architecture).

O objetivo principal do WCF é permitir que analistas e desenvolvedores criem aplicações voltadas para computação distribuída.

O WCF possui ainda um conjunto de bibliotecas (classes) que permitem aos desenvolvedores criar estas aplicações para funcionarem sob o sistema operacional Windows. Ele é, na verdade,  uma evolução ao Web Service, isso porque ele agrupa várias tecnologias para resolver o problema na comunicação dos dados.

WCF Data Services é uma forma de expormos um modelo de dados do Entity Framework através de uma interface REST. O DS é uma extensão do WCF (isto é, pode ser hospedado como qualquer serviço WCF). É uma tecnologia que visa facilitar o acesso a dados tanto de aplicações Web comuns como AJAX, como Silverlight e .NET.

Para expor os dados via WCF Data Services, é preciso que se tenha uma fonte de dados que suporte consultas utilizando LINQ, isto é, que implemente IQueryable/IUpdatable. Resumidamente, a aplicação pede um endereço para o serviço, e este endereço é traduzido como uma consulta LINQ para que então, o provedor de dados retorne os dados de interesse.

Parece meio confuso mas na verdade o WCF Data Service nos dá as seguintes possibilidades:

  • fornece uma API que nos permite criar e consumir dados através de HTTP utilizando serviços RESTful. 
  • suporta todas as operações de banco de dados através de URI. 
  • é capaz de expor um Modelo de Entidade através de uma URI. 
  • suporta CRUD. 
  • pode ser consumido por qualquer aplicação em diferentes tipos de clientes como Windows, SilverLight, Web, AJAX e Console.

Mas vamos deixar de conversa e vermos na prática como isso funciona!

Crie um projeto do tipo Asp .Net Web Application, dê o nome de “WebAppWCFDataService”.

Feito isso, precisamos adicionar nosso WCF Data Service, para isto, clique com o botão direito na solução e adicione um item do tipo “WCF Data Service”.

wcfdata01

Figura 01 – inclusão de novo item

 

wcfdata02

Figura 02 – item do tipo WCF Data Service

 

Seu projeto deverá estar como na figura 03.

wcfdata03

Figura 03 – Solução

 

Precisamos agora de nossa fonte de dados. Estou utilizando o NorthWind para este exemplo, caso não possua um banco de dados, você pode baixá-lo aqui.

Não irei entrar em detalhes de como instalar o banco pois está fora do escopo deste post.

Vamos criar nosso mapeamento relacional, para isso adicione um item do tipo ADO .Net Entity Data Model. Ele irá apresentar várias telas para que você escolha o banco, as tabelas, o nome da conexão e o contexto, basta seguir conformes as figuras 04, 05, 06 e 07.

wcfdata04

Figura 04 – inclusão do entity data model

 

wcfdata05

Figura 05 – Seleção do tipo de Modelo

 

wcfdata06

Figura 06 – String de conexão e Context

 

wcfdata07

Figura 07 – Escolha das tabelas

 

Pronto, agora que já temos nosso mapeamento precisamos informar ao data service qual é nosso contexto e o que ele deverá disponibilizar.

Abra o arquivo WcfDataService1.svc.cs, ele deve estar como o código da listagem 1

Listagem 01

using System;
using System.Collections.Generic;
using System.Data.Services;
using System.Data.Services.Common;
using System.Linq;
using System.ServiceModel.Web;
using System.Web;

namespace WebAppWCFDataSerevice
{
    public class WcfDataService1 : DataService< /* TODO: put your data source class name here */ >
    {
        // This method is called only once to initialize service-wide policies.
        public static void InitializeService(DataServiceConfiguration config)
        {
            // TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc.
            // Examples:
            // config.SetEntitySetAccessRule("MyEntityset", EntitySetRights.AllRead);
            // config.SetServiceOperationAccessRule("MyServiceOperation", ServiceOperationRights.All);
            config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
        }
    }
}

Altere a linha

public class WcfDataService1 : DataService< /* TODO: put your data source class name here */ >

Para

public class WcfDataService1 : DataService<NorthwindEntities>

 

Remova as duas barras da linha

// config.SetEntitySetAccessRule("MyEntityset", EntitySetRights.AllRead);


Altere para

config.SetEntitySetAccessRule("*", EntitySetRights.All);


O que fizemos foi dizer ao nosso Data Service qual é a classe que contem o mapeamento para nossas entidades e definir as permissões para elas, no caso, definimos que todas as entidades não irão possuir qualquer restrição “EntitySetRights.All”

Compile o aplicativo, se tudo estiver correto ele deverá apresentar a tela como na figura 08.

wcfdata08

Figura 08 – aplicativo sendo executado


Bem, a princípio nada demais ok? Errado! Percebam que nosso serviço expos nossas entidades, assim podemos realizar pesquisas sem a necessidade de criarmos métodos para isso. Mas como isso é possível? Simples, como havia falado, o WCF Data Service nos fornece maneiras de realizarmos consultas em banco através de URI´s.

Vejam os exemplos abaixo.

Listar todos os Clientes
http://localhost:26482/WcfDataService1.svc/Customers

Listar todos os Clientes de São Paulo
http://localhost:26482/WcfDataService1.svc/Customers?$filter=Region eq 'SP'

Listar os 10 primeiros Produtos
http://localhost:26482/WcfDataService1.svc/Products?$top=10

Mas você deve estar se perguntando. De onde você tirou estas URI´s? É simples existe uma tabela com parâmetros de pesquisa para as URI´s conforme abaixo. Não estão todos ai, mas os mais importantes.


Operações

orderby - realiza a ordenação por um parâmetro qualquer
top - seleciona a quantidade de registros informada
filter - realiza a pesquisa com base em algum filtro

 

Operadores

eq - igual
ne - não igual
gt - maior que
ge - maior ou igual
lt - menor que
le - menor ou igual
and - AND lógico
or - OR lógico
not - NOT lógico

 

Como puderam ver, o WCF Data Service é uma nova maneira de expormos nossas entidades sem a necessidade de criarmos métodos para realizar as diversas pesquisa e operações necessárias tornando assim nossa produtividade muito maior.

Num próximo post veremos como consumir nossos Data Services, além de darmos uma passada nas permissões de acesso às entidades e segurança na troca de informações.

E lembrem-se “Informação só tem sentido se for compartilhada!”

Enjoy!!

Tags:

WCF | Desenvolvimento Web | C# | Asp .Net | Data Service

XNA - Pong

Pessoal, a pedido de uma galerinha estou publicando novamente um artigo que fiz a algum tempo sbore XNA, neste artigo irei montar um jogo muito famoso – PONG, que foi o primeiro game a ser desenvolvido comercialmente.

Bem espero que aproveitem!

O videogame Pong, criado por Nolan Bushnell e Ted Dabney, foi instalado num console ligado a um monitor e era movido a moedinhas.
Para testar a sua nova criação, os inventores deixaram a máquina em um bar em San Francisco, Califórnia. No dia seguinte, tiveram uma surpresa quando checaram a máquina e viram que ela estava lotada de moedas. Pong havia sido jogado durante toda a noite. Aquelas moedas mudaram toda a perspectiva que os criadores tinham em relação a seu invento. Aí estava o primeiro videogame lucrativo da história.

Nolan Bushnell e Ted Dabney perceberam que, em vez de vender a idéia para alguém, era melhor abrir o seu próprio negócio. E em 27 de Junho de 1972, a empresa Atari foi fundada.

O objetivo era acertar a esfera (bola) com sua raquete (barra vertical) e lançar para o campo adversário, marcando ponto quando a bola passasse ao campo adversário.

xna_parte_II_00

Figura 1 – Primeira máquina desenvolvida com o jogo Pong - (Fonte: Wikipedia)

Agora que sabemos um pouco da historia de nosso “futuro” jogo, vamos ao que interessa.

Mas antes de começarmos a codificar, vamos dar uma olhada em alguns tópicos que serão cruciais, entre os mais importantes podemos destacar: Sistema de Coordenadas, Sprite, Sistema de Colisão e entrada de dados (Teclado), para que possamos entender da melhor forma possível o desenvolvimento de um simples jogo.

Sistema de Coordenadas

O sistema de coordenadas adotado pelo XNA não é o sistema convencional onde temos nosso ponto inicial na parte inferior esquerda conforme figura 2. No XNA nosso ponto inicial começa no canto superior esquerdo conforme figura 3.

xna_parte_II_01
Figura 2 – Sistema de Coordenadas Convencional

xna_parte_II_03

Figura 3 – Sistema de Coordenadas adotado pelo XNA Framework

Assim sendo, para que possamos desenhar uma imagem na tela, devemos indicar sua origem partindo do canto superior esquerdo, o mesmo vale para a imagem, onde seu ponto de origem não é o centro da imagem, mas o canto superior esquerdo conforme figura 4.

xna_parte_II_02

Figura 4 – Posicionamento de Imagem

Como podem ver na figura 4, posicionamos nossa imagem no ponto X = 80 e Y = 80 partindo do ponto de origem X = 0 e Y = 0 no canto superior esquerdo. Para trabalharmos com nosso sistema de coordenadas iremos utilizar a classe Vector2, onde iremos informar as posições X e Y respectivamente. Como exemplo temos a listagem 1.

Listagem 1 – Posicionando uma imagem na tela

Vector2 posicao = new Vector2( 80, 80 );


Sprite

Em computação gráfica, um sprite (do latim spiritus, significando "duende", "fada") é um objeto gráfico bi ou tridimensional que se move numa tela sem deixar traços de sua passagem (como se fosse um "espírito").

Os sprites foram inventados originalmente como um método rápido de animação de várias imagens agrupadas numa tela, em jogos de computador bidimensionais, usando hardware especial. A medida que a performance dos computadores melhorou, esta otimização tornou-se desnecessária e o termo evoluiu para referir-se especificamente às imagens bidimensionais que eram integradas numa determinada cena, isto é, figuras geradas por hardware ou software eram todas referenciadas como sprites. A medida que gráficos tridimensionais tornaram-se mais comuns, o termo passou a descrever uma técnica elementar de simulação de imagens em 2.5D ou 3D que prescinde do uso de renderizações complexas

Para que possamos carregar uma Sprite, utilizamos a classe Texture2D. E para desenharmos nosso Sprite na tela utilizamos a classe  SpriteBatch

Sistema de Colisão

O Sistema de colisão nada mais é do que verificarmos se dois objetos estam se sobrepondo. Esse tipo de sistema pode ser simples (BoundingBox e BoudingSphere) ou mais complexos (Colisão por Pixel). A colisão entre Sprites pode ser realizadas de várias formas entre as mais comuns podemos citar:
BoundingBox: a colisão é feita utilizando o retângulo da imagem (2D), onde verificamos se ambos estão se sobrepondo. Não é uma das melhores formas de se detectar uma colisão, porque pode ocorrer das imagens não ocuparem todo o retângulo, mas é uma das mais rapidas e mais facil de implementar. Uma das vantagem de se usar BoundingBox é que se migrarmos nosso código 2D para 3D não iremos ter q implemtar novamente o sistema de colisão já que o mesmo trabalha com coordenadas 3D.

xna_parte_II_04 xna_parte_II_05
Figura 5 – Não há colisão Figura 6 – Há colisão


BoundingSphere: bem parecido com a colisão por retângulo, porém utilizamos a forma esférica para testar se as imagens estão se sobrepondo. Assim como a anterior é de fácil implementação e bem ágil.

xna_parte_II_06 xna_parte_II_07

Figura 7 – Não há colisão

Figura 8 – Há colisão

Pixel: a colisão por pixel é realizada utilizando a área transparente de uma imagem. Nessas imagens a área transparente não é desenhada na tela, pois elas possuem o valor alpha 0 (zero). Assim basta testar se nas áreas da imagens estam desenhados pixels transparentes, sem colisão, ou pixels coloridos (colisão). A técnica de colisão por pixel é uma das melhores a ser utilizada, pois nos dá uma precisão maior na hora de trabalharmos com imágens que possuem muitas curvas e áreas onde a colisão por boundingbox ou boundingsphere não ficariam legal. É um método mais lento e mais difícil de ser implemtnado.

Na figura 9 vemos que mesmo as imagens estando sobrepostas não há colisão, isso porque somente a parte transparente estam se sobrepondo.

Já na figura 10 a imagens em si estam se sobrepondo.

Num próximo artigos veremos um pouco mais a fundo sobre física e como chegamos a certos cálculos, como por exemplo para detectar a colisão entre as esferas, que utiliza o Teorema de Pitágoras (A2 = B2 + C2), a colisão por Pixel e alguns calculos como velocidade, distancia, gravidade para que possamos implementar melhor nossos jogos.

xna_parte_II_08 xna_parte_II_09

Figura 9 – Colisão por Pixel - Não há colisão

xna_parte_II_10

Figura 10 – Colisão por Pixel – Há colisão

Entrada de dados

O XNA possui três tipos de classes para se trabalhar com entrada de dados que são:

- Keyboard – responsável por tratar eventos do teclado;
- Mouse – trata eventos do mouse;
- GamePad – trata eventos de controles como é o caso do Xbox 360.

Cada classe acima possui seu método estático para pegar o estado dispositivo. São eles:

- KeyboardState;
- MouseState;
- GamePadState.

Apesar de termos estes tipos de classes o XNA ainda deixa a desejar quanto a entrada de dados ou seja, se estivermos verificando se uma tecla foi pressionada não temos como saber se ele continua pressionada ou não, para que possamos fazer isto é necessário que armazenemos o “estado” anterior da tecla e testemos o mesmo posteriormente.

Coisas como essa a meu ver, devem estar prestes a serem disponibilizadas numa próxima versão do XNA, pelo menos é o que esperamos.

O pequeno trecho de código da listagem 2 nos mostra como implementar uma verificação de tecla.

Listagem 2 – Verificando a tecla pressionada.

KeyboardState keys = Keyboard.GetState();

  if ( keys.IsKeyDown( Keys.Up ) )
  {
     // Executa alguma ação com base na tecla precionada
  }

 

Agora que incrementamos nosso conhecimento sobre coordenadas, sprite, colisão e enrtrada de dados, vamos dar inicio ao desenvolvimento de nosso jogo. A parte de Orientação a Objetos não séra abordada neste artigo, assim sendo aconselho aos menos experientes a procurarem no Google assuntos relacionados a OO (Orientação a Objeto).

Criando as classes Objetos, Bola e Player

Crie um novo projeto do tipo WindowsGame e de o nome de “Pong”.

Feito isso nós iremos criar mais três novas classes que são: Objetos.cs, Bola.cs e Player.cs, irei descrever e explicar cada classe separadamente

Começaremos por modelar nossa classe Objetos.cs

Esta classe irá conter as propriedades e métodos comuns as nossas outras duas classes (Player e Bola).

Após adicionar a nova classe dê o nome a ela de “Objetos.cs” e acrescente o código conforme a listagem 3, não esqueça de adicionar em todas a classes criadas os namespaces: Microsoft.Xna.Framework e Microsoft.Xna.Framework.Graphics;

Listagem 3 – Classe base Objetos.cs.

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Pong
{
    class Objetos
    {
        private Vector2 posicao;
        private Vector2 tamanho;
        private Vector2 velocidade;
        private Texture2D textura;

        public Vector2 Posicao
        {
            get { return posicao; }
            set { posicao = value; }
        }

        public Vector2 Tamanho
        {
            get { return tamanho; }
            set { tamanho = value; }
        }
 

        public Vector2 Velocidade
        {
            get { return velocidade; }
            set { velocidade = value; }
        }

        public Texture2D Textura
        {
            get { return textura; }
            set { textura = value; }
        }

        public Rectangle Retangulo
        {
            get { return new Rectangle( (int)posicao.X, (int)posicao.Y, (int)tamanho.X, (int)tamanho.Y ); }
        }
    }
}

Na classe Objetos.cs temos as seguinte variáveis e seus respectivos get/set que serão herdados por outras classes:

será responsável por receber a posição do objeto
 Vector2 posicao;

receberá o tamanho do objeto
 Vector2 tamanho;
irá controlar a vbelocidade de cada objeto
 Vector2 velocidade;
receberá uma textura referente a cada objeto
 Texture2D textura;

     será utilizado na verificação de colisão entre os objetos
     public Rectangle Retangulo
     {
       get { return new Rectangle( (int)posicao.X, (int)posicao.Y, (int)tamanho.X, (int)tamanho.Y ); }
     }

Está será nossa classe base.

Adicione uma nova classe e dê o nome de Player.cs. Feito isso herde a classe Objeto, nossa classe player ficará como na listagem 4.

Listagem 4 – Classe Player.cs

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace Pong
{
    class Player : Objetos
    {
    }
}

Reparem que nossa classe player é bem simples, não implementaremos nada nela apenas iremos herdar as propriedades da classe Objeto, para os menos experientes isso é uma das vantagens da OO (Orientação a Objeto), Herança.

Por último adicione uma nova classe e dê o nome de Bola.cs. Esta classe irá conter apenas dois métodos e uma propriedade. Reparem o código da listagem 5, irei explicar cada método separadamente.

Listagem 5 – Classe Bola.cs

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;

namespace Pong
{
    class Bola : Objetos
    {
        private float aceleracao = 0.15f;

        public float Aceleracao
        {
            get { return aceleracao; }
        }

        /// <summary>
        /// Move a bola
        /// </summary>
        /// <param name="gw"></param>
        /// <param name="pontosPlayer1"></param>
        /// <param name="pontosPlayer2"></param>
        public void mover( GameWindow gw, ref int pontosPlayer1, ref int pontosPlayer2 )
        {
            this.Posicao += new Vector2( Velocidade.X, Velocidade.Y );

            if ( this.Posicao.X <= 0 )
            {
                pontosPlayer2++;
                this.Velocidade *= new Vector2( -1, 1 );
            }
            if ( this.Posicao.X >= gw.ClientBounds.Width - this.Textura.Width )
            {
                pontosPlayer1++;
                this.Velocidade *= new Vector2( -1, 1 );
            }
            if ( this.Posicao.Y < 0 || this.Posicao.Y >= ( gw.ClientBounds.Height - this.Textura.Height ) )
                this.Velocidade *= new Vector2( 1, -1 );

        }

        /// <summary>
        /// Checa a colisão
        /// </summary>
        /// <param name="jogador1"></param>
        /// <param name="jogador2"></param>
        public void checaColisao( Rectangle jogador1, Rectangle jogador2 )
        {
            bool colidiu = false;

            if ( this.Velocidade.X < 0 )
            {
                colidiu = this.Retangulo.Left   < jogador1.Right &&
                             this.Retangulo.Right  > jogador1.Left &&
                             this.Retangulo.Top    < jogador1.Bottom &&
                             this.Retangulo.Bottom > jogador1.Top;
            }
            else
            {
                colidiu = this.Retangulo.Left   < jogador2.Right &&
                             this.Retangulo.Right  > jogador2.Left &&
                             this.Retangulo.Top    < jogador2.Bottom &&
                             this.Retangulo.Bottom > jogador2.Top;
            }

            if ( colidiu )
            {
                //Muda a direção
                this.Velocidade *= new Vector2( -1, 1 );
            }
        }
    }
}

O primeiro método é responsável por mover a bola na tela. Nele passamos como parâmetros GameWindow que é na verdade a classe no qual iremos extrair o tamanho da tela utilizada no jogo, nela iremos pegar o comprimento e a largura da tela através do ClientBounds.Width e ClientBounds.Height. Os outros dois parâmetros dizem respeito à pontuação dos jogadores. Utilizamos parâmetros ref para passarmos por referência o valor dos pontos se cada jogador.

Reparem que utilizamos mais uma vez a classe Vector2 para incrementarmos a posição e a velocidade da bola na tela. Fizemos também a checagem do posicionamento da bola para que a mesma não saia da tela, caso sua posição no eixo X seja menor ou igual a 0 multiplicamos a velocidade da bola por -1 (menos um), isso fará com que a bola passe a decrementar o valor da posição invertendo sua direção. Note que multiplicamos dentro do vetor o X por -1 e o Y por 1, isso porque estamos verificando o eixo X, para o eixo Y faremos quase a mesma coisa, testamos o posicionamento da bola e se for o caso, invertemos sua posição.

Listagem 6 – Descrição do método mover

this.Posicao += new Vector2( Velocidade.X, Velocidade.Y );

   if ( this.Posicao.X <= 0 )
   {
  pontosPlayer2++;
       this.Velocidade *= new Vector2( -1, 1 );
   }

O código da listagem 7 nos mostra o método que será responsável por checar a colisão da bola com os bastões (Players).

Utilizamos a propriedade left, right, top e botton da classe Rectangle para verificarmos se a bola faz alguma interseção com o bastão, caso haja esse contato retornamos um valor true, ou seja, há colisão entre o player1 e a bola.

Para o player dois, realizamos os mesmo testes, porém antes de o fazermos, verificamos se a velocidade da bola é menor que zero isto porque, caso ela tenha velocidade negativa, sua trajetória será em direção ao player 1 (player da esquerda) sendo necessário realizar o teste em apenas um dos retângulos.

Detectada a colisão, multiplicamos nosso vetor velocidade por -1 no eixo X para alterarmos a direção da bola.

Listagem 7 – Teste de Colisão

if ( this.Velocidade.X < 0 )
  {
     colidiu = this.Retangulo.Left   < jogador1.Right &&
               this.Retangulo.Right  > jogador1.Left &&
               this.Retangulo.Top    < jogador1.Bottom &&
               this.Retangulo.Bottom > jogador1.Top;
  }
  else
  {
     colidiu = this.Retangulo.Left   < jogador2.Right &&
               this.Retangulo.Right  > jogador2.Left &&
               this.Retangulo.Top    < jogador2.Bottom &&
               this.Retangulo.Bottom > jogador2.Top;
  }

  if ( colidiu )
  {
     //Muda a direção
     this.Velocidade *= new Vector2( -1, 1 );
  }

Implementando Game.cs

Agora que já temos nossas classes criadas, vamos dar inicio a implementação da classe Game.cs, responsável pelo nosso jogo.

Primeiro vamos adicionar ao nosso projeto quatro imagens que serão a bola, o player, a imagem com os números e a rede. Nosso jogo irá ter a aparência da figura 11.

xna_parte_II_11

Figura 11 – Tela do XNA Pong

 

Adicionado as imagens, vá para a classe Game.cs e adicione os seguintes dados conforme listagem 8.

Listagem 8 – Variáveis do jogo

//Classe Bola
    Bola bola;

    //Classe Player
    Player player1;
    Player player2;

    //Textura a ser utilizada na classe Bola
    Texture2D imgBola;

    //Textura a ser utilizada na classe Player
    Texture2D imgPlayer;

    //Textura para gerar os pontos dos jogadores
    Texture2D imgNumeros;

     //Textura da "rede"
     Texture2D imgRede;

     //Array que irá receber cada "fatia" da imagem com seu respectivo número
     Rectangle[] pontos = null;

     //Pontos dos jogadores
     int pontosPlayer1;
     int pontosPlayer2;

Vamos agora inicializar nossas variáveis. Conforme explicado no artigo anterior, temos um lugar propício para realizar esta tarefa, no Initialize() da classe Game, conforme listagem 9. Observem que inicializamos os pontos de nossos jogadores com o valor 0. Mas ok e o que tem isso a ver? Bem, isso não quer dizer que os ogadores irão começar com pontuação zerada, mas sim por que iremos passar os pontos de cada jogador por referência (ref) e nesse caso é obrigatória a inicialização das variáveis, caso estivéssemos utilizando out ao invés de ref isso não seria necessário.

Listagem 9 – Inicializando as variáveis

protected override void Initialize()
{
   bola = new Bola();
   player1 = new Player();
   player2 = new Player();
   pontosPlayer1 = 0;
   pontosPlayer2 = 0;

   base.Initialize();
}

Após inicializarmos nossas variáveis, vamos carregar nossos objetos(imagens), posicionar nosso players, a bola e preencher o array com as imagens do número para a pontuação. A listagem 10 nos mostra o método LoadContent por completo.

Listagem 10 – Método LoadContent

protected override void LoadContent()
{
   // Create a new SpriteBatch, which can be used to draw textures.
   spriteBatch = new SpriteBatch( GraphicsDevice );

   // TODO: use this.Content to load your game content here

  //Carregando as imagens que serão utilizadas no jogo
  imgBola = Content.Load<Texture2D>( "bola" );
  imgPlayer = Content.Load<Texture2D>( "player" );
  imgNumeros = Content.Load<Texture2D>( "numbers" );
  imgRede = Content.Load<Texture2D>( "rede" );
    
    
    //Definições da Bola
    //Aqui definimos as propriedades da bola como Tamanho, Posição inicial na tela
    //velocidade e qual a textura a ser utilizada.
    //Note tambem que utilizamos a classe Windows.ClientBound para posicionarmos
    //nossa bola no centro da tela
    bola.Tamanho = new Vector2( imgBola.Width, imgBola.Height );
    bola.Posicao = new Vector2( ( Window.ClientBounds.Width / 2 ) - ( bola.Tamanho.X / 2 ), ( Window.ClientBounds.Height / 2 ) - ( bola.Tamanho.Y / 2 ) );
    bola.Velocidade = new Vector2( 5.0f, 4.0f );
    bola.Textura = imgBola;
    
    //Player 1
    //Assim como a bola, definimos também os dados de cada player
    player1.Tamanho = new Vector2( imgPlayer.Width, imgPlayer.Height );
    player1.Posicao = new Vector2( 10f, ( Window.ClientBounds.Height / 2 - imgPlayer.Height / 2 ) );
    player1.Velocidade = new Vector2( 0, 5 );
    player1.Textura = imgPlayer;
    
    //Player 2
    player2.Tamanho = new Vector2( imgPlayer.Width, imgPlayer.Height );
    player2.Posicao = new Vector2( ( Window.ClientBounds.Width - 10 - imgPlayer.Width ), ( Window.ClientBounds.Height / 2 - imgPlayer.Height / 2 ) );
    player2.Velocidade = new Vector2( 0, 5 );
    player2.Textura = imgPlayer;
    
    pontos = new Rectangle[10];
    
    //Carrega um array com a definição dos numeros
    //Aqui realizamos o preenchimento de nosso array com seus
    //respectivos valores para termos a posição certa de cada
    //fatia dentro da imagem original
    for ( int i = 0; i < 10; i++ )
    {
      pontos[i] = new Rectangle( i * 45, 0, 45, 75 );
    }
}
        

Como podem observar, não fizemos nada demais, apenas informamos onde estão nossas imagens, onde posicionaremos nossos players, a bola, a rede e os pontos.

Para que possamos ver nosso resultado até o presente momento, vamos programar nosso método Draw(), assim poderemos ver como ficou nossa tela. A explicação encontra-se dentro do próprio método, como mostrado na listagem 11.


Listagem 11
– Método Draw

protected override void Draw( GameTime gameTime )
{
  graphics.GraphicsDevice.Clear( Color.Black );

  // TODO: Add your drawing code here
  //Inicializa o SpriteBatch no modo AlphaBlend, ou seja
  //faz com que as imagens sejam desenhadas com o fundo transparente
  spriteBatch.Begin( SpriteBlendMode.AlphaBlend );

  //Posicionamos nossa "rede" no centro da tela
  spriteBatch.Draw( imgRede, new Vector2( ( Window.ClientBounds.Width * .5f ), 0 ), Color.White );

  //Desenhamos a pontuação do player 1 +- a 1/4 de tela
  spriteBatch.Draw( imgNumeros, new Vector2( ( Window.ClientBounds.Width * .22f ), 10 ), pontos[pontosPlayer1], Color.White );
  //Desenhamos a pontuação do player 2 +- a 3/4 de tela
  spriteBatch.Draw( imgNumeros, new Vector2( ( Window.ClientBounds.Width * .72f ), 10 ), pontos[pontosPlayer2], Color.White );
        
  //Desenhamos nossa "bola" apesar de ser quadrada..rsrs
  spriteBatch.Draw( bola.Textura, bola.Retangulo, Color.White );

  //Desenhamos os Player, um de cada lado
  spriteBatch.Draw( player1.Textura, player1.Retangulo, Color.White );
  spriteBatch.Draw( player2.Textura, player2.Retangulo, Color.White );

  spriteBatch.End();
      
  base.Draw( gameTime );
}

Calma pessoal, falta pouco agora..hehehehe. Vamos programar agora o método que será responsável por “mover” os players na tela. Dei o nome de movePlayers() (bem intuitivo não?). Como vimos anteriormente assim como nossos outros métodos, programar o método para mover os players não é nada complicado, apenas verificamos qual tecla foi pressionada a partir daí executamos a ação desejada.

Vamos ver em detalhes o que ele faz.

Primeiro criamos a instância chamada Keys que será responsável por receber a tecla que foi pressionada.

Depois fazemos a verificação, no caso, qual seria esta tecla.

O primeiro teste verifica se foi a tecla “seta para cima” ou “up”, a tecla pressionada, depois verificamos se a posição do Player1 não é menor que zero, isso porque caso a posição seja menor que zero no eixo Y, quer dizer que ele está fora da tela, nesse caso ele pára na posição atual e invertemos a velocidade para que agora ele possa descer, conforme a listagem 12.

Listagem 12 – Método movePlayers – Verificando Player1

public void movePlayers()
  {
      KeyboardState keys = Keyboard.GetState();

      //Player 1
      if ( keys.IsKeyDown( Keys.Up ) )
      {
          if ( player1.Posicao.Y < 0 )
             player1.Posicao = new Vector2( player1.Posicao.X, 0 );
           player1.Posicao -= player1.Velocidade;
      }

O segundo teste verifica se foi a tecla “seta para baixo” ou “down”, a tecla pressionada, depois verificamos se a posição do Player1 mais seu tamanho (height) não é maior que o tamanho da tela, isso para ele não ultrapassar a parte inferior da tela, depois invertemos a velocidade para que agora ele possa subir, conforme a listagem 13.

Listagem 13 – Método movePlayers – Verificando Player1

if ( keys.IsKeyDown( Keys.Down ) )
    {
      if ( player1.Posicao.Y > Window.ClientBounds.Height - player1.Textura.Height )
          player1.Posicao = new Vector2( player1.Posicao.X, ( Window.ClientBounds.Height - imgPlayer.Height ) );
         player1.Posicao += player1.Velocidade;
    }

Para o player 2 fazemos as mesmas verificações, conforme a listagem 14

Listagem 14 – Método movePlayers – Verificando Player2

//Player 2
    if ( keys.IsKeyDown( Keys.W ) )
    {
       if ( player2.Posicao.Y < 0 )
          player2.Posicao = new Vector2( player2.Posicao.X, 0 );
        player2.Posicao -= player2.Velocidade;
    }

    if ( keys.IsKeyDown( Keys.S ) )
    {
       if ( player2.Posicao.Y > Window.ClientBounds.Height - player2.Textura.Height  )
          player2.Posicao = new Vector2( player2.Posicao.X, ( Window.ClientBounds.Height - imgPlayer.Height ) );
        player2.Posicao += player2.Velocidade;
    }
}

Para finalizarmos, basta adicionar o código da listagem 15 ao método Update().

Listagem 15 – Método Update

// TODO: Add your update logic here
      bola.checaColisao( player1.Retangulo, player2.Retangulo );
      bola.mover( Window, ref pontosPlayer1, ref pontosPlayer2 );
      movePlayers();

      if ( pontosPlayer1 > 9 || pontosPlayer2 > 9 )
      {
          pontosPlayer1 = 0;
          pontosPlayer2 = 0;
      }

Muito simples, checamos se há colisão da bola com alguns dos players. O próprio método checaColisao faz com que assim que nossa bola colida com algum player automaticamente ela mude de direção.

Já o método move da classe Bola, move nossa bola ao longo da tela e caso haja colisão inverte sua direção na tela.

Chamamos o método movePlayers para darmos movimento aos nosso jogadores através do teclado.

E por fim, fazemos a verificação da pontuação de cada player, caso esta ultrapasse o valor 9, nós zeramos o contador de ambos os players.

Bem, ai está nosso primeiro jogo em XNA.

Conclusão

Como puderam ver o XNA Framework foi criado especialmente para facilitar a vida dos programadores de jogos, pois tanto o profissional quanto o entusiasta podem usufruir de uma ferramenta muito poderosa, e por encapsular funções do DirectX ele nos dá mobilidade para pensarmos mais no jogo do que na forma de codificarmos, sem contar que podemos muito bem ver nossas criações serem rodadas em um console como é o caso do XBox 360 (quem nunca teve vontade de ver seu jogo rodando em um console de vídeo game?).

O desenvolvimento de nosso Pong foi algo bem simples, mas que nos leva a entender um pouco da lógica por trás de um jogo.

Espero que tenham gostado do artigo.

Tags:

Games | C# | XNA

Community Lauch BH

Bem pessoal, chegamos ao final de nosso Community Lauch em Belo Horizonte.

Agradecemos a todos que estiveram nos acompanhando durante os últimos 60 dias... tivemos a oportunidade de falar para diferentes públicos, em diversas faculdades: Uni-BH, Infórium, PUC e Izabela Hendrix.

Dedicamos horas de nosso esforço para trazer para a Comunidade Mineira estas informações sobre os novos produtos Microsoft para desenvolvimento, em especial Visual Studio 2010 e SQLServer 2008 R2. E temos certeza que foi muito proveitoso para todos que participaram, direta ou indiretamente.

Para aqueles que não puderam participar, fiquem atentos pois teremos muito mais pela frente.

Não deixem de nos acompanhar e faça parte dessa grande comunidade, acesse www.dotnetraptors.com.br e www.100loop.com.br

 

Valeu galera!!

 

100610_201952100610_200512

100609_205808100609_205828DSC02270DSC02271

DSC02272DSC02268

DSC02273100609_214731

Tags:

Asp .Net | C# | Desenvolvimento Web | MVC 2.0